Show current location in Cesium - cesiumjs

I'm using cesium map
<!DOCTYPE html>
<html lang="en">
<head>
<title>Draw</title>
<script src="../../Cesium/Cesium.js"></script>
<style>
#import url(../../Cesium/Widgets/widgets.css);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
Cesium.BingMapsApi.defaultKey = 'XXXXXXXXXX';
var viewer = new Cesium.Viewer('cesiumContainer', {infoBox : false, selectionIndicator : false });
var color;
var camera = viewer.camera;
var polyline;
var drawing = false;
var positions = [];
var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(
function (click) {
if (drawing) {
color.alpha = 0.6;
viewer.entities.add({
polygon: {
hierarchy : {
positions : positions
},
material : color,
outline : true
}
});
viewer.entities.remove(polyline);
positions = [];
} else {
color = Cesium.Color.fromRandom({alpha : 1.0});
polyline = viewer.entities.add({
polyline : {
positions : new Cesium.CallbackProperty(function(){
return positions;
}, false),
material : color
}
});
}
drawing = !drawing;
},
Cesium.ScreenSpaceEventType.LEFT_CLICK
);
handler.setInputAction(
function (movement) {
var surfacePosition = camera.pickEllipsoid(movement.endPosition);
if (drawing && Cesium.defined(surfacePosition)) {
positions.push(surfacePosition);
}
},
Cesium.ScreenSpaceEventType.MOUSE_MOVE
);
camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(-100, 40, 10000)});
</script>
</body>
</html>
I want to show current location on the map and it should zoom that location from where the user is accessing the map. Is it possible in cesium, Thanks

Possible.
Here is a Sandcastle link.

check cesium api doc here.
https://cesium.com/learn/cesiumjs/ref-doc/Viewer.html?classFilter=viewer#zoomTo
viewer.zoomTo(any entity object you want to focus, option)

Related

How to merge multiple intersecting polygons

I'm trying to merge or group polygons using turfjs.
The following illustration shows what it looks like before and how it should look like after the merge.
I'm working with Openlayers and Typescript and both have Polygons hence the alias TurfPolygon etc.
I'm convert my OpenLayers Polygons to Turfjs Polygons first, so I can use Turf functions.
const mergedIds: number[] = [];
const t0 = performance.now();
for (const object of arrayTurfObjects) {
if (mergedIds.find(x => x === object.properties.mergedId)) {
continue;
}
let mergeResultPolygon: TurfFeature<TurfPolygon> | null = null;
for (let indexNested = 0; indexNested < arrayTurfObjects.length; indexNested++) {
const nestedObject = arrayTurfObjects[indexNested];
if(nestedObject === object){
continue;
}
if(mergedIds.find(x => x === nestedObject.properties.mergedId)){
continue;
}
if(mergeResultPolygon){
if(booleanIntersects(mergeResultPolygon, nestedObject)){
mergeResultPolygon = TurfUnion(mergeResultPolygon, nestedObject) as TurfFeature<TurfPolygon, any>;
mergedIds.push(nestedObject.properties.mergedId);
indexNested = 0;
}
} else {
if(booleanIntersects(object, nestedObject)){
mergeResultPolygon = TurfUnion(object, nestedObject) as TurfFeature<TurfPolygon, any>;
mergedIds.push(nestedObject.properties.mergedId);
indexNested = 0;
}
}
}
if (mergeResultPolygon) {
const polygon = new Polygon(mergeResultPolygon.geometry.coordinates);
const feature = new Feature(polygon);
feature.setStyle(new Style({
stroke: new Stroke({
color: ColorCode.BLACK,
width: 5,
}),
fill: new Fill({
color: 'rgba(255,0,0, 0.6)',
}),
}));
//Here is an function to add the mergeResultPolygon to the map to check if it's correct
}
}
const t1 = performance.now();
console.log((t1 - t0) + ' milliseconds.');
I'm iterating over the same array, which contains the polygons twice.
First I check if the polygon is already merged, so I can skip, and I'm declaring my merge result polygon.
Then comes the nested loop where I skip the polygon, if it's the same as the polygon in my outer loop and if its already merged.
To start my merging process I'm looking for the first polygon that intersects the current outer loop polygon, so I can set a value for my mergeResultPolygon.
After that I just merge more polygons to that variable.
As you can see I have to reset the nested loop, so I can iterate again.
I'm doing this, because I don't have any kind of order, so the previous polygon could intersect the merge Result Polygon.
My problem is that I'm doing this on the client side and the performance is not that great.
Is there a better solution for this problem?
Demonstration of getting merged polygons from a union of all polygons
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.5.0/css/ol.css" type="text/css">
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.5.0/build/ol.js"></script>
<script src="https://unpkg.com/#turf/turf#6.3.0/turf.min.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script type="text/javascript">
var count = 200;
var features = new Array(count);
var e = 4500000;
for (var i = 0; i < count; ++i) {
var coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
features[i] = new ol.Feature(new ol.geom.Polygon.fromExtent(ol.extent.buffer(coordinates.concat(coordinates), 200000)));
}
var source = new ol.source.Vector({
features: features,
});
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: source
})
],
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
var result;
format = new ol.format.GeoJSON();
source.forEachFeature(function(feature) {
var turfPolygon = format.writeFeatureObject(feature);
if (!result) {
result = turfPolygon;
} else {
result = turf.union(result, turfPolygon);
}
});
var results = [];
var olResult = format.readFeature(result);
if (olResult.getGeometry().getType() === 'MultiPolygon') {
olResult.getGeometry().getPolygons().forEach(function(polygon) {
results.push(new ol.Feature(polygon));
});
} else {
results.push(olResult);
}
map.addLayer(
new ol.layer.Vector({
source: new ol.source.Vector({
features: results,
}),
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
})
})
})
);
</script>
</body>
</html>

How to close a docking panel when opening a new instance in the autodesk forge viewer api?

I have been displaying data attached to a few nodes in the viewer using a custom docking panel. I have attached the listener event on the click of those nodes and instantiating a new docking panel instance.
It works fine. New custom panel get's displayed every time I click on a node. There is a close handler attached to the close button which closes the panel.
But, how can I close the existing panel before opening a new one? I tried to initialize the viewer with initializeCloseHandler so that wherever clicked the opened docking panel closes. But it's not working. I am unable to get the viewer instance there.
Now both instances get added one above another. I am attaching a screenshot of it for reference - https://i.stack.imgur.com/lT5Y1.png
How can I achieve this? This is what I have tried so far,
Sample code:
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
<meta charset="utf-8">
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css" type="text/css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.min.js"></script>
<style>
body {
margin: 0;
}
#forgeViewer {
width: 100%;
height: 100%;
margin: 0;
background-color: #F0F8FF;
}
</style>
</head>
<body>
<div id="forgeViewer"></div>
</body>
<script>
var viewer;
var options = {
env: 'AutodeskProduction',
api: 'derivativeV2', // for models uploaded to EMEA change this option to 'derivativeV2_EU'
getAccessToken: function(onTokenReady) {
var token = 'access token here';
var timeInSeconds = 3600; // Use value provided by Forge Authentication (OAuth) API
onTokenReady(token, timeInSeconds);
}
};
SimplePanel = function(parentContainer, id, title, content, x, y)
{
this.content = content;
Autodesk.Viewing.UI.DockingPanel.call(this, parentContainer, id, title,{shadow:false});
// Auto-fit to the content and don't allow resize. Position at the coordinates given.
//
this.container.style.height = "250px";
this.container.style.width = "450px";
this.container.style.resize = "auto";
this.container.style.left = x + "px";
this.container.style.top = y + "px";
this.container.style.zIndex = 2;
};
SimplePanel.prototype = Object.create(Autodesk.Viewing.UI.DockingPanel.prototype);
SimplePanel.prototype.constructor = SimplePanel;
SimplePanel.prototype.initialize = function()
{
this.title = this.createTitleBar(this.titleLabel || this.container.id);
this.container.appendChild(this.title);
this.container.appendChild(this.content);
this.initializeMoveHandlers(this.container);
this.closer = document.createElement("span");
this.closer = this.createCloseButton();
this.initializeCloseHandler(this.closer);
this.container.appendChild(this.closer);
var op = {left:false,heightAdjustment:45,marginTop:0};
this.scrollcontainer = this.createScrollContainer(op);
// console.log("id - "+viewer.model.getFragmentList().fragments.fragId2dbId);
console.log(viewer.getSelection());
var id = viewer.getSelection();
var dataItem;
var data = [
{
id:"2648",
name:"Chiller",
temp:"300 deg",
serviceReq:true,
reservations:"3"
},
{
id:"2228",
name:"Door",
temp:"150 deg",
serviceReq:false,
reservations:"4"
},
{
id:"2198",
name:"Cooler",
temp:"400 deg",
serviceReq:true,
reservations:"2"
}
]
data.forEach(item => {
if(item.id == id){
dataItem = item;
}
})
var html = [
'<div class="uicomponent-panel-controls-container">',
'<div class="panel panel-default">',
'<table class="table table-hover table-responsive" id = "clashresultstable">',
'<thead>',
'<th>Key</th><th>Value</th>',
'</thead>',
'<tbody>',
'<tr><td>ID</td><td>'+dataItem.id+'</td></tr>',
'<tr><td>Name</td><td>'+dataItem.name+'</td></tr>',
'<tr><td>Temperature</td><td>'+dataItem.temp+'</td></tr>',
'<tr><td>Service Requests</td><td>'+dataItem.serviceReq+'</td></tr>',
'<tr><td>Reservations</td><td>'+dataItem.reservations+'</td></tr>',
'</tbody>',
'</table>',
'</div>',
'</div>'
].join('\n');
$(this.scrollContainer).append(html);
this.initializeMoveHandlers(this.title);
};
Autodesk.Viewing.Initializer(options, function() {
var htmlDiv = document.getElementById('forgeViewer');
viewer = new Autodesk.Viewing.GuiViewer3D(htmlDiv);
var startedCode = viewer.start();
if (startedCode > 0) {
console.error('Failed to create a Viewer: WebGL not supported.');
return;
}
console.log('Initialization complete, loading a model next...');
});
var documentId = 'urn:(urn)';
Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
function onDocumentLoadSuccess(viewerDocument) {
var defaultModel = viewerDocument.getRoot().getDefaultGeometry();
viewer.loadDocumentNode(viewerDocument, defaultModel);
viewer.addEventListener( Autodesk.Viewing.SELECTION_CHANGED_EVENT, event=>{
var content = document.createElement('div');
var mypanel = new SimplePanel(NOP_VIEWER.container,'mypanel','My Panel',content,20,20);
mypanel.setVisible(true);
})
}
// function closer(){ -- tried to attach this handler to the viewer instance,but didn't work
// SimplePanel.setVisible(false);
// }
function onDocumentLoadFailure() {
console.error('Failed fetching Forge manifest');
}
</script>
</html>````
[1]: https://i.stack.imgur.com/lT5Y1.png
I would advise you to use a viewer extension to manage your panel in order to reuse the same panel instance. Here are some examples for you:
https://github.com/yiskang/forge-au-sample/blob/master/properties/scripts/AdnPropsPanel.js
https://github.com/yiskang/forge-au-sample/blob/master/model-structure/scripts/AdnStructurePanel.js
If you don't want to implement an extension, you may store the mypanel variable somewhere, and use if statement to destroy the old panel before creating a new one.
var mypanel = null;
// Other code snippet
viewer.addEventListener( Autodesk.Viewing.SELECTION_CHANGED_EVENT, event=> {
if( mypanel != null ) {
//NOP_VIEWER.container.removeChild( mypanel.container );
mypanel.uninitialize();
mypanel = null;
}
var content = document.createElement('div');
mypanel = new SimplePanel(NOP_VIEWER.container,'mypanel','My Panel',content,20,20);
mypanel.setVisible( true );
})
Hope it helps!

Chrome extension web inspector

I want to write a chrome extension where it is possible to mark features on a website and save it in a table, for this, I want to use the Chrome web inspector.
Unfortunately, I am new to this area (chrome plugins) and therefore I am looking for help (links, tutorials, related work etc.) to use the web inspector in my own extension.
Simple example on this website https://ieeexplore.ieee.org/document/1005630.
My idea is to mark for example the date of publication, and the plugin write the complete div to a table.
actually, I found a simple solution.
Sample
http://g.recordit.co/5CCFjXpe8J.gif
It's only a small part of my tool to keep it simple.
The main idea comes from Google Chrome Extension: highlight the div that the mouse is hovering over
'iframe' is the injected sidebar
marker.js contains the script to mark divs
manifest.json
{
"name": "Feature extractor",
"version": "1.0",
"description": "Feature extractor from website",
"permissions": ["activeTab", "declarativeContent", "storage", "contextMenus", "<all_urls>", "tabs"],
"browser_action": {},
"web_accessible_resources": ["iframe.html","iframe.js"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"css": [
"marker.css"
],
"js": [
"js/jquery-1.8.3.min.js",
"marker.js"
]
}
],
"manifest_version": 2
}
background.js
'use strict';
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostEquals: 'developer.chrome.com'},
})],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
});
// sidebar
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id,"toggle");
})
});
// message passing
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
console.log(request);
callback({'request':request});
});
// context menu
var labels = ['author','date','abstract']
for(var label in labels) {
console.log(labels[label])
chrome.contextMenus.create({id: labels[label], "title": labels[label], "contexts":['all']});
}
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId == labels[0]) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id,labels[0]);
})
}
});
iframe.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/jquery.mobile-1.2.1.min.css" />
<script src="js/jquery-1.8.3.min.js"></script>
<script src="js/jquery.mobile-1.2.1.min.js"></script>
<script src="iframe.js"></script>
</head>
<body>
<button id="send">
send
</button>
<div id="responses">
</div>
</body>
</html>
I need the jQuery.fn.. script to identify the selected div Get unique selector of element in Jquery
iframe.js
// unique selector
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
window.addEventListener('DOMContentLoaded', function () {
var callback = function (data) {
$("#responses").append("<div>" + data + "</div>");
};
var send = function () {
chrome.runtime.sendMessage(Date(), callback);
}
chrome.runtime.onMessage.addListener(function(msg, data){
if (msg.command == "append-author") {
$("#responses").append("<div>" + msg.el + "</div>")
}
})
document.getElementById('send').addEventListener('click', send);
});
Google Chrome Extension: highlight the div that the mouse is hovering over
marker.js
// Unique ID for the className.
var MOUSE_VISITED_CLASSNAME = 'crx_mouse_visited';
var MOUSE_MARKED_CLASSNAME = 'crx_mouse_marked';
// Previous dom, that we want to track, so we can remove the previous styling.
var prevDOM = null;
// Mouse listener for any move event on the current document.
document.addEventListener('mousemove', function (e) {
let srcElement = e.srcElement;
// Lets check if our underlying element is a IMG.
if (prevDOM != srcElement && srcElement.nodeName == 'DIV' ) {
// For NPE checking, we check safely. We need to remove the class name
// Since we will be styling the new one after.
if (prevDOM != null) {
prevDOM.classList.remove(MOUSE_VISITED_CLASSNAME);
}
// Add a visited class name to the element. So we can style it.
srcElement.classList.add(MOUSE_VISITED_CLASSNAME);
// The current element is now the previous. So we can remove the class
// during the next ieration.
prevDOM = srcElement;
// console.info(srcElement.currentSrc);
// console.dir(srcElement);
}
}, false);
var iframe = document.createElement('iframe');
iframe.style.background = "green";
iframe.id = "comm-test-container";
iframe.style.height = "100%";
iframe.style.width = "0px";
iframe.style.position = "fixed";
iframe.style.top = "0px";
iframe.style.right = "0px";
iframe.style.zIndex = "9000000000000000000";
iframe.frameBorder = "none";
iframe.src = chrome.extension.getURL("iframe.html")
document.body.appendChild(iframe);
function toggle(){
if(iframe.style.width == "0px") {
iframe.style.width="400px";
} else {
iframe.style.width="0px";
}
}
chrome.runtime.onMessage.addListener(function(msg, sender){
if(msg == "toggle"){
toggle();
}
if(msg == "author") {
prevDOM.classList.add(MOUSE_MARKED_CLASSNAME);
chrome.runtime.sendMessage({command:"append-author",el:prevDOM.innerHTML,selector:$(prevDOM).getPath()}, function(response) {});
}
})
// find unique selector
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
marker.css
.crx_mouse_visited {
background-clip: #bcd5eb!important;
outline: 1px dashed #e9af6e!important;
z-index : 0!important;
}
.crx_mouse_marked {
background-clip: #bcd5eb!important;
outline: 5px solid #e9af6e!important;
z-index : 0!important;
}

Save and load the position of dynamically created, draggable elements(jQuery-UI)

I am in the process of developing an offline web application that mimics a magnet board.
I can dynamicly create the draggable Items and drag them around the screen. Now I want to be able to save the positions and content(just one image per element) of my elements into a file and later load it again.
I want it in a file because it needs to be interchangable between different users.
Here is the javascript of how i dynamicly create my elements:
$("#item1").mousedown(function (e){
var newpin = document.createElement("DIV");
var pinimage = document.createElement("IMG");
pinimage.setAttribute("src", "Media/2D_Container_Alfa.jpg");
pinimage.setAttribute("height", "70px");
newpin.setAttribute("position","relative");
newpin.setAttribute("top","20px");
newpin.setAttribute("left","140px");
newpin.setAttribute("display","block");
newpin.setAttribute("class", "draggable ui-draggable ui-draggable-handle");
newpin.appendChild(pinimage);
document.body.appendChild(newpin);});
TLDR.: I want to save the configuration on my magnet board and be able to load previously saved configurations
You may consider some newer code:
function makePin(event, t, l){
var $pin = $("<div>", {
class: "pin draggable ui-draggable ui-draggable-handle"
}).css({
position: "relative",
top: (t !== undefined ? t + "px" : "20px"),
left: (l !== undefined ? l + "px" : "140px"),
disply: "block"
}).appendTo(".pin-area");
var $pinImage = $("<img>", {
src: "Media/2D_Container_Alfa.jpg"
}).css({
height: "70px"
}).appendTo($pin);
return $pin;
}
function getPins(){
var pins = [];
$(".pin-area .pin").each(function(ind, el){
var pos = $(el).offset();
var imgSrc = $("img", el).attr("src");
pins.push({
top: pos.top,
left: pos.left,
src: imgSrc
});
});
return pins;
}
$("#item1").mousedown(function (e){
makePin(e);
});
Since you did not include any HTML in your post, I would consider the following in relationship to the code above:
<body>
<div class="pin-area">
</div>
</body>
You also did not indicate what would initiate the saving of this data once collected or how it would be loaded. one way to do this is to update once any drag action is completed, binding to stop. You also did not mention where you wanted to store this data. The following makes some assumptions that you will be using Draggable and LocalStorage.
$(".pin").on("dragstop", function(e, ui){
localStorage.setItem("pins", JSON.stringify(getPins()));
});
This will update a locally stored variable with JSON text each time a pin is moved.
function loadPins(){
var pins = JSON.parse(localStorage.getItems("pins"));
if(pins !== undefined){
$.each(pins, function(k, v){
makePin(null, v.top, v.left);
});
}
}
This function would then be able to load those pins. You'd want to execute this function just before you close your jQuery block. A small example of it all put together. You will need to do more testing and adjusting to fit your needs.
$(function() {
function makePin(event, t, l) {
var $pin = $("<div>", {
class: "pin draggable ui-draggable ui-draggable-handle"
}).css({
position: "relative",
top: (t !== undefined ? t + "px" : "20px"),
left: (l !== undefined ? l + "px" : "140px"),
disply: "block"
}).appendTo(".pin-area");
var $pinImage = $("<img>", {
src: $(".example-image").attr("src")
}).css({
height: "70px"
}).appendTo($pin);
$pin.draggable();
return $pin;
}
function getPins() {
var pins = [];
$(".pin-area .pin").each(function(ind, el) {
var pos = $(el).offset();
var imgSrc = $("img", el).attr("src");
pins.push({
top: pos.top,
left: pos.left,
src: imgSrc
});
});
return pins;
}
function loadPins() {
var pins = JSON.parse(localStorage.getItems("pins"));
if (pins !== undefined) {
$.each(pins, function(k, v) {
makePin(null, v.top, v.left);
});
}
}
$(".pin").on("dragstop", function(e, ui) {
localStorage.setItem("pins", JSON.stringify(getPins()));
});
$(".pin-area").mousedown(function(e) {
makePin(e);
});
loadPins();
});
.pin-area {
width: 1200px;
height: 630px;
border: 1px solid #222;
background-image: url("https://www2.lbl.gov/assets/img/maps/sitemap.jpg");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="pin-area">
</div>
<p>Example Pin:</p><img class="example-image" style="height: 70px;" src="https://www.clker.com/cliparts/r/R/m/q/E/6/orange-pin-th.png">

Geowebcache + Openlayers3 with XYZ source: error by zooming the map

I'm trying the do the same map as here:
Openlayers3: tile grid incorrect with pixelratio=3 using Geoserver/Geowebcache as backend
but using the TMS protocol instead of WMS.
The map works good but there is a little problem by zooming in, only by changing from zoom level 4 to zoom level 5: the map seems to "jump upwards". The problem occurs with all pixel ratio.
This is my source code. Any help is appreciated:
<!DOCTYPE html>
<html>
<head>
<title>WMS Tiles</title>
<link rel="stylesheet" href="https://openlayers.org/en/v3.19.1/css/ol.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://openlayers.org/en/v3.19.1/build/ol.js"></script>
</head>
<style type="text/css">
body { font-family: sans-serif; font-weight: bold; font-size: .8em; }
body { border: 0px; margin: 0px; padding: 0px; }
</style>
<body>
<div id="map" class="map" style="width: 85%; height: 85%;border: 0px; padding: 0px"> </div>
<script>
console.log("DOMContentLoaded");
try {
var config = {
"bounds": {
"left" : 475279.2689196961,
"bottom" : 5473193.572300382,
"right" : 476655.1750108673,
"top" : 5474594.636365802
}
};
var bounds = [config.bounds.left, config.bounds.bottom, config.bounds.right, config.bounds.top];
var resolutions = [
1.4,
0.7,
0.35,
0.175,
0.0875,
0.04375,
];
var tilePixelRatio = 1;
if (ol.has.DEVICE_PIXEL_RATIO > 2.5) {
tilePixelRatio = 3;
} else if (ol.has.DEVICE_PIXEL_RATIO > 1.5) {
tilePixelRatio = 2;
}
var matrixSet = 'unihd15';
// available gridsets in backend: unihd15, unihd15_512 and unihd15_768
if (tilePixelRatio > 1){
matrixSet = matrixSet + '_' + (256 * tilePixelRatio).toString();
}
var res_length = resolutions.length;
var matrixIds = new Array(res_length );
for (var z = 0; z < res_length ; ++z) {
matrixIds[z] = matrixSet + ':'+ z;
}
console.log('matrixSet is: ' + matrixSet);
console.log(matrixIds);
console.log(matrixIds[0] + ' '+ resolutions[0]);
console.log('Center: ' + ol.extent.getCenter(bounds));
console.log('Pixel ratio: ' + window.devicePixelRatio);
console.log('Bounds: ' + bounds);
console.log('TopLeft: ' + ol.extent.getTopLeft(bounds));
var projection = new ol.proj.Projection({
code: 'EPSG:32632',
units: 'm',
extent: [166021.4431, 0.0000, 833978.5569, 9329005.1825]
});
var tileGrid = new ol.tilegrid.TileGrid({
extent: bounds,
resolutions: resolutions,
origin: ol.extent.getTopLeft(bounds),
tileSize: [256, 256]
});
var view = new ol.View({
extent: bounds,
zoom: 0,
center: ol.extent.getCenter(bounds),
projection: projection,
resolutions: resolutions
});
var layerName = 'unihd15:unihd15_0_basemap';
var tms_source = new ol.source.XYZ({
projection: projection,
tileGrid: tileGrid,
tilePixelRatio: tilePixelRatio,
url: 'http://ssp.deepmap.de/geo/gwc/service/tms/1.0.0/' + layerName + '#' + matrixSet + '#png/{z}/{x}/{-y}.png'
});
var layer = new ol.layer.Tile({
source: tms_source,
extent: bounds
});
var map = new ol.Map({
projection: projection,
controls: ol.control.defaults(
{
rotate: false,
attributionOptions: {
collapsible: false
}
}
),
view: view,
layers: [layer],
target: 'map'
});
console.log("no error");
} catch (error) {
console.log("error");
console.log(error);
}
</script>
</body>
</html>
Usually this kind of problem comes from an incorrect extent. To get the correct extent, go to http://ssp.deepmap.de/geo/gwc/service/tms/1.0.0/unihd15:unihd15_0_basemap#EPSG:32632#png (currently not working on your GeoServer, but that's the URL), and use the <BoundingBox> from there.