mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 22:56:11 +03:00
Merge pull request #910 from avandecreme/home-clip
Fix home bounds with clipping. Fix #891
This commit is contained in:
commit
5785d10cbb
@ -287,6 +287,26 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
return this.getBounds();
|
return this.getBounds();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bounds of the displayed part of the tiled image.
|
||||||
|
* @param {Boolean} [current=false] Pass true for the current location,
|
||||||
|
* false for the target location.
|
||||||
|
* @returns {$.Rect} The clipped bounds in viewport coordinates.
|
||||||
|
*/
|
||||||
|
getClippedBounds: function(current) {
|
||||||
|
var bounds = this.getBounds(current);
|
||||||
|
if (this._clip) {
|
||||||
|
var ratio = this._worldWidthCurrent / this.source.dimensions.x;
|
||||||
|
var clip = this._clip.times(ratio);
|
||||||
|
bounds = new $.Rect(
|
||||||
|
bounds.x + clip.x,
|
||||||
|
bounds.y + clip.y,
|
||||||
|
clip.width,
|
||||||
|
clip.height);
|
||||||
|
}
|
||||||
|
return bounds;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
|
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
|
||||||
*/
|
*/
|
||||||
|
126
src/viewport.js
126
src/viewport.js
@ -341,7 +341,9 @@ $.Viewport.prototype = {
|
|||||||
}, margins);
|
}, margins);
|
||||||
|
|
||||||
this._updateContainerInnerSize();
|
this._updateContainerInnerSize();
|
||||||
this.viewer.forceRedraw();
|
if (this.viewer) {
|
||||||
|
this.viewer.forceRedraw();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1085,8 +1087,18 @@ $.Viewport.prototype = {
|
|||||||
return this.viewportToImageCoordinates(viewerX.x, viewerX.y);
|
return this.viewportToImageCoordinates(viewerX.x, viewerX.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.viewportToImageCoordinates] is not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.viewportToImageCoordinates] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.viewportToImageCoordinates instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageCoordinates
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.viewportToImageCoordinates(viewerX, viewerY, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._viewportToImageDelta(
|
return this._viewportToImageDelta(
|
||||||
@ -1119,8 +1131,18 @@ $.Viewport.prototype = {
|
|||||||
return this.imageToViewportCoordinates(imageX.x, imageX.y);
|
return this.imageToViewportCoordinates(imageX.x, imageX.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.imageToViewportCoordinates] is not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.imageToViewportCoordinates] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.imageToViewportCoordinates instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageCoordinates
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.imageToViewportCoordinates(imageX, imageY, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var point = this._imageToViewportDelta(imageX, imageY);
|
var point = this._imageToViewportDelta(imageX, imageY);
|
||||||
@ -1150,6 +1172,21 @@ $.Viewport.prototype = {
|
|||||||
rect = new $.Rect(imageX, imageY, pixelWidth, pixelHeight);
|
rect = new $.Rect(imageX, imageY, pixelWidth, pixelHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.viewer) {
|
||||||
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.imageToViewportRectangle] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.imageToViewportRectangle instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.imageToViewportRectangle
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.imageToViewportRectangle(
|
||||||
|
imageX, imageY, pixelWidth, pixelHeight, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var coordA = this.imageToViewportCoordinates(rect.x, rect.y);
|
var coordA = this.imageToViewportCoordinates(rect.x, rect.y);
|
||||||
var coordB = this._imageToViewportDelta(rect.width, rect.height);
|
var coordB = this._imageToViewportDelta(rect.width, rect.height);
|
||||||
return new $.Rect(
|
return new $.Rect(
|
||||||
@ -1183,6 +1220,21 @@ $.Viewport.prototype = {
|
|||||||
rect = new $.Rect(viewerX, viewerY, pointWidth, pointHeight);
|
rect = new $.Rect(viewerX, viewerY, pointWidth, pointHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.viewer) {
|
||||||
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.viewportToImageRectangle] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.viewportToImageRectangle instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageCoordinates
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.viewportToImageRectangle(
|
||||||
|
viewerX, viewerY, pointWidth, pointHeight, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var coordA = this.viewportToImageCoordinates(rect.x, rect.y);
|
var coordA = this.viewportToImageCoordinates(rect.x, rect.y);
|
||||||
var coordB = this._viewportToImageDelta(rect.width, rect.height);
|
var coordB = this._viewportToImageDelta(rect.width, rect.height);
|
||||||
return new $.Rect(
|
return new $.Rect(
|
||||||
@ -1224,10 +1276,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} pixel
|
* @param {OpenSeadragon.Point} pixel
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
windowToImageCoordinates: function( pixel ) {
|
windowToImageCoordinates: function(pixel) {
|
||||||
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.windowToImageCoordinates] the viewport must have a viewer.");
|
||||||
var viewerCoordinates = pixel.minus(
|
var viewerCoordinates = pixel.minus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
return this.viewerElementToImageCoordinates( viewerCoordinates );
|
return this.viewerElementToImageCoordinates(viewerCoordinates);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1236,10 +1290,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} pixel
|
* @param {OpenSeadragon.Point} pixel
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
imageToWindowCoordinates: function( pixel ) {
|
imageToWindowCoordinates: function(pixel) {
|
||||||
var viewerCoordinates = this.imageToViewerElementCoordinates( pixel );
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.imageToWindowCoordinates] the viewport must have a viewer.");
|
||||||
|
var viewerCoordinates = this.imageToViewerElementCoordinates(pixel);
|
||||||
return viewerCoordinates.plus(
|
return viewerCoordinates.plus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1295,10 +1351,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} pixel
|
* @param {OpenSeadragon.Point} pixel
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
windowToViewportCoordinates: function( pixel ) {
|
windowToViewportCoordinates: function(pixel) {
|
||||||
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.windowToViewportCoordinates] the viewport must have a viewer.");
|
||||||
var viewerCoordinates = pixel.minus(
|
var viewerCoordinates = pixel.minus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
return this.viewerElementToViewportCoordinates( viewerCoordinates );
|
return this.viewerElementToViewportCoordinates(viewerCoordinates);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1306,10 +1364,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} point
|
* @param {OpenSeadragon.Point} point
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
viewportToWindowCoordinates: function( point ) {
|
viewportToWindowCoordinates: function(point) {
|
||||||
var viewerCoordinates = this.viewportToViewerElementCoordinates( point );
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.viewportToWindowCoordinates] the viewport must have a viewer.");
|
||||||
|
var viewerCoordinates = this.viewportToViewerElementCoordinates(point);
|
||||||
return viewerCoordinates.plus(
|
return viewerCoordinates.plus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1324,9 +1384,19 @@ $.Viewport.prototype = {
|
|||||||
* target zoom.
|
* target zoom.
|
||||||
* @returns {Number} imageZoom The image zoom
|
* @returns {Number} imageZoom The image zoom
|
||||||
*/
|
*/
|
||||||
viewportToImageZoom: function( viewportZoom ) {
|
viewportToImageZoom: function(viewportZoom) {
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.viewportToImageZoom] is not accurate with multi-image.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.viewportToImageZoom] is not ' +
|
||||||
|
'accurate with multi-image.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageZoom
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.viewportToImageZoom(viewportZoom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageWidth = this._contentSizeNoRotate.x;
|
var imageWidth = this._contentSizeNoRotate.x;
|
||||||
@ -1348,9 +1418,19 @@ $.Viewport.prototype = {
|
|||||||
* target zoom.
|
* target zoom.
|
||||||
* @returns {Number} viewportZoom The viewport zoom
|
* @returns {Number} viewportZoom The viewport zoom
|
||||||
*/
|
*/
|
||||||
imageToViewportZoom: function( imageZoom ) {
|
imageToViewportZoom: function(imageZoom) {
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.imageToViewportZoom] is not accurate with multi-image.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.imageToViewportZoom] is not accurate ' +
|
||||||
|
'with multi-image.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.imageToViewportZoom
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.imageToViewportZoom(imageZoom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageWidth = this._contentSizeNoRotate.x;
|
var imageWidth = this._contentSizeNoRotate.x;
|
||||||
|
44
src/world.js
44
src/world.js
@ -375,34 +375,40 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
var oldContentSize = this._contentSize ? this._contentSize.clone() : null;
|
var oldContentSize = this._contentSize ? this._contentSize.clone() : null;
|
||||||
var oldContentFactor = this._contentFactor || 0;
|
var oldContentFactor = this._contentFactor || 0;
|
||||||
|
|
||||||
if ( !this._items.length ) {
|
if (!this._items.length) {
|
||||||
this._homeBounds = new $.Rect(0, 0, 1, 1);
|
this._homeBounds = new $.Rect(0, 0, 1, 1);
|
||||||
this._contentSize = new $.Point(1, 1);
|
this._contentSize = new $.Point(1, 1);
|
||||||
this._contentFactor = 1;
|
this._contentFactor = 1;
|
||||||
} else {
|
} else {
|
||||||
var bounds = this._items[0].getBounds();
|
var item = this._items[0];
|
||||||
this._contentFactor = this._items[0].getContentSize().x / bounds.width;
|
var bounds = item.getBounds();
|
||||||
var left = bounds.x;
|
this._contentFactor = item.getContentSize().x / bounds.width;
|
||||||
var top = bounds.y;
|
var clippedBounds = item.getClippedBounds();
|
||||||
var right = bounds.x + bounds.width;
|
var left = clippedBounds.x;
|
||||||
var bottom = bounds.y + bounds.height;
|
var top = clippedBounds.y;
|
||||||
var box;
|
var right = clippedBounds.x + clippedBounds.width;
|
||||||
for ( var i = 1; i < this._items.length; i++ ) {
|
var bottom = clippedBounds.y + clippedBounds.height;
|
||||||
box = this._items[i].getBounds();
|
for (var i = 1; i < this._items.length; i++) {
|
||||||
this._contentFactor = Math.max(this._contentFactor, this._items[i].getContentSize().x / box.width);
|
item = this._items[i];
|
||||||
left = Math.min( left, box.x );
|
bounds = item.getBounds();
|
||||||
top = Math.min( top, box.y );
|
this._contentFactor = Math.max(this._contentFactor,
|
||||||
right = Math.max( right, box.x + box.width );
|
item.getContentSize().x / bounds.width);
|
||||||
bottom = Math.max( bottom, box.y + box.height );
|
clippedBounds = item.getClippedBounds();
|
||||||
|
left = Math.min(left, clippedBounds.x);
|
||||||
|
top = Math.min(top, clippedBounds.y);
|
||||||
|
right = Math.max(right, clippedBounds.x + clippedBounds.width);
|
||||||
|
bottom = Math.max(bottom, clippedBounds.y + clippedBounds.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._homeBounds = new $.Rect( left, top, right - left, bottom - top );
|
this._homeBounds = new $.Rect(left, top, right - left, bottom - top);
|
||||||
this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor,
|
this._contentSize = new $.Point(
|
||||||
|
this._homeBounds.width * this._contentFactor,
|
||||||
this._homeBounds.height * this._contentFactor);
|
this._homeBounds.height * this._contentFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._contentFactor !== oldContentFactor || !this._homeBounds.equals(oldHomeBounds) ||
|
if (this._contentFactor !== oldContentFactor ||
|
||||||
!this._contentSize.equals(oldContentSize)) {
|
!this._homeBounds.equals(oldHomeBounds) ||
|
||||||
|
!this._contentSize.equals(oldContentSize)) {
|
||||||
/**
|
/**
|
||||||
* Raised when the home bounds or content factor change.
|
* Raised when the home bounds or content factor change.
|
||||||
* @event metrics-change
|
* @event metrics-change
|
||||||
|
@ -206,10 +206,12 @@
|
|||||||
propEqual(image.getClip(), clip, 'clip is set correctly');
|
propEqual(image.getClip(), clip, 'clip is set correctly');
|
||||||
|
|
||||||
Util.spyOnce(viewer.drawer, 'setClip', function(rect) {
|
Util.spyOnce(viewer.drawer, 'setClip', function(rect) {
|
||||||
ok(true, 'drawer.setClip is called');
|
var homeBounds = viewer.viewport.getHomeBounds();
|
||||||
var pixelRatio = viewer.viewport.getContainerSize().x / image.getContentSize().x;
|
var canvasClip = viewer.viewport
|
||||||
var canvasClip = clip.times(pixelRatio * OpenSeadragon.pixelDensityRatio);
|
.viewportToViewerElementRectangle(homeBounds);
|
||||||
propEqual(rect, canvasClip, 'clipping to correct rect');
|
var precision = 0.00000001;
|
||||||
|
Util.assertRectangleEquals(rect, canvasClip, precision,
|
||||||
|
'clipping should be ' + canvasClip);
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -220,6 +222,39 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('getClipBounds', function() {
|
||||||
|
var clip = new OpenSeadragon.Rect(100, 200, 800, 500);
|
||||||
|
|
||||||
|
viewer.addHandler('open', function() {
|
||||||
|
var image = viewer.world.getItemAt(0);
|
||||||
|
var bounds = image.getClippedBounds();
|
||||||
|
var expectedBounds = new OpenSeadragon.Rect(1.2, 1.4, 1.6, 1);
|
||||||
|
propEqual(bounds, expectedBounds,
|
||||||
|
'getClipBounds should take clipping into account.');
|
||||||
|
|
||||||
|
image = viewer.world.getItemAt(1);
|
||||||
|
bounds = image.getClippedBounds();
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(1, 2, 2, 2);
|
||||||
|
propEqual(bounds, expectedBounds,
|
||||||
|
'getClipBounds should work when no clipping set.');
|
||||||
|
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
clip: clip,
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
width: 2
|
||||||
|
}, {
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
width: 2
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest('opacity', function() {
|
asyncTest('opacity', function() {
|
||||||
|
|
||||||
|
@ -238,6 +238,57 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('getHomeBoundsWithMultiImages', function() {
|
||||||
|
function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
new OpenSeadragon.Rect(0, 0, 4, 4),
|
||||||
|
viewport.getHomeBounds(),
|
||||||
|
0.00000001,
|
||||||
|
"Test getHomeBoundsWithMultiImages");
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 2
|
||||||
|
}, {
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 3,
|
||||||
|
y: 3,
|
||||||
|
width: 1
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('getHomeBoundsWithMultiImagesAndClipping', function() {
|
||||||
|
function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
new OpenSeadragon.Rect(1, 1, 4, 4),
|
||||||
|
viewport.getHomeBounds(),
|
||||||
|
0.00000001,
|
||||||
|
"Test getHomeBoundsWithMultiImagesAndClipping");
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 2,
|
||||||
|
clip: new OpenSeadragon.Rect(500, 500, 500, 500)
|
||||||
|
}, {
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 4,
|
||||||
|
y: 4,
|
||||||
|
width: 1
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
asyncTest('getHomeZoom', function() {
|
asyncTest('getHomeZoom', function() {
|
||||||
reopenViewerHelper({
|
reopenViewerHelper({
|
||||||
property: 'defaultZoomLevel',
|
property: 'defaultZoomLevel',
|
||||||
|
Loading…
Reference in New Issue
Block a user