2019-11-04 10:59:08 +01:00
|
|
|
/**
|
2019-12-11 16:45:26 +01:00
|
|
|
* MapProjection contains the informations about how
|
2019-11-04 10:59:08 +01:00
|
|
|
* a Map has to be interpreted. What are the bounds of the
|
|
|
|
* map and how to translate coordinates into
|
|
|
|
* image positions.
|
|
|
|
*
|
|
|
|
* @class
|
2019-12-11 16:45:26 +01:00
|
|
|
* @see {@link mapprojection.html}
|
2019-11-04 10:59:08 +01:00
|
|
|
*/
|
2019-12-11 16:45:26 +01:00
|
|
|
export class MapProjection {
|
2019-11-04 10:59:08 +01:00
|
|
|
/**
|
2019-12-11 16:45:26 +01:00
|
|
|
* Creates instance of MapProjection
|
2019-11-04 10:59:08 +01:00
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
|
|
|
|
* @param {object}[opts] - Addiditonal options.
|
2019-12-09 18:15:28 +01:00
|
|
|
* @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
|
2019-11-04 10:59:08 +01:00
|
|
|
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
|
|
|
|
*/
|
|
|
|
constructor(projection, opts = {}) {
|
|
|
|
this.opts = Object.assign(
|
|
|
|
{
|
|
|
|
translate: { x: 0, y: 0 }
|
|
|
|
},
|
|
|
|
opts
|
|
|
|
)
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
this._projection = projection
|
2019-11-04 10:59:08 +01:00
|
|
|
|
2019-11-25 18:04:11 +01:00
|
|
|
if (this.opts.clip) {
|
2019-11-04 10:59:08 +01:00
|
|
|
let _cmin = this.projection.forward(this.opts.clip.min)
|
|
|
|
let _cmax = this.projection.forward(this.opts.clip.max)
|
|
|
|
|
|
|
|
// Swaps the y values, Mercator has it's origin bottom right,
|
|
|
|
// browser coordinates start top right.
|
|
|
|
let cmin = { x: _cmin.x, y: _cmax.y }
|
|
|
|
let cmax = { x: _cmax.x, y: _cmin.y }
|
|
|
|
|
|
|
|
this.clipExt = {
|
|
|
|
coords: {
|
|
|
|
min: this.opts.clip.min,
|
|
|
|
max: this.opts.clip.max
|
|
|
|
},
|
|
|
|
point: {
|
|
|
|
min: cmin,
|
|
|
|
max: cmax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:29:59 +01:00
|
|
|
/**
|
2019-12-11 16:45:26 +01:00
|
|
|
* The projection used by the map projection.
|
2019-12-11 15:29:59 +01:00
|
|
|
*
|
|
|
|
* @member {Projection}
|
|
|
|
* @readonly
|
2019-12-11 16:45:26 +01:00
|
|
|
* @memberof MapProjection
|
2019-12-11 15:29:59 +01:00
|
|
|
*/
|
|
|
|
get projection() {
|
|
|
|
return this._projection
|
|
|
|
}
|
|
|
|
|
2019-11-25 18:04:11 +01:00
|
|
|
/**
|
|
|
|
* Transforms a pixel point on the map to a geographical coordinate.
|
|
|
|
*
|
2019-12-09 18:15:28 +01:00
|
|
|
* @public
|
2019-12-11 15:29:59 +01:00
|
|
|
* @param {Point} point - A pixel position on the map.
|
|
|
|
* @returns {CoordinatePoint} A geographical coordinate.
|
2019-12-11 16:45:26 +01:00
|
|
|
* @memberof MapProjection
|
2019-11-25 18:04:11 +01:00
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
toCoordinates(point) {
|
2019-11-25 18:04:11 +01:00
|
|
|
if (this.opts.clip) {
|
2019-11-04 10:59:08 +01:00
|
|
|
let min = this.clipExt.point.min
|
|
|
|
let max = this.clipExt.point.max
|
|
|
|
|
|
|
|
let width = max.x - min.x
|
|
|
|
let height = max.y - min.y
|
|
|
|
|
|
|
|
point.x *= width
|
|
|
|
point.y *= height
|
|
|
|
|
|
|
|
point.x += min.x
|
|
|
|
point.y += min.y
|
|
|
|
}
|
|
|
|
|
|
|
|
let coordinates = this.projection.backward(point)
|
|
|
|
|
|
|
|
if (this.opts.translate) {
|
|
|
|
coordinates.x -= this.opts.translate.x
|
|
|
|
coordinates.y -= this.opts.translate.y
|
|
|
|
}
|
|
|
|
|
|
|
|
return coordinates
|
|
|
|
}
|
|
|
|
|
2019-11-25 18:04:11 +01:00
|
|
|
/**
|
|
|
|
* Transform a geographical coordinate to a pixel point on the map.
|
|
|
|
*
|
2019-12-09 18:15:28 +01:00
|
|
|
* @public
|
2019-12-11 15:29:59 +01:00
|
|
|
* @param {CoordinatePoint} coordinates - A point in the form of {x:lat,y:lng}.
|
|
|
|
* @returns {Point} A pixel position on the map.
|
2019-12-11 16:45:26 +01:00
|
|
|
* @memberof MapProjection
|
2019-11-25 18:04:11 +01:00
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
toPixel(coordinates) {
|
|
|
|
let coords = { x: coordinates.x, y: coordinates.y }
|
|
|
|
if (this.opts.translate) {
|
|
|
|
coords.x += this.opts.translate.x
|
|
|
|
coords.y += this.opts.translate.y
|
|
|
|
}
|
|
|
|
|
|
|
|
let point = this.projection.forward(coords)
|
|
|
|
|
|
|
|
if (this.opts.clip) {
|
|
|
|
let min = this.clipExt.point.min
|
|
|
|
let max = this.clipExt.point.max
|
|
|
|
|
|
|
|
let clippedPoint = {
|
|
|
|
x: point.x - min.x,
|
|
|
|
y: point.y - min.y
|
|
|
|
}
|
|
|
|
|
|
|
|
let width = max.x - min.x
|
|
|
|
let height = max.y - min.y
|
|
|
|
|
|
|
|
point.x = clippedPoint.x / width
|
|
|
|
point.y = clippedPoint.y / height
|
|
|
|
}
|
|
|
|
|
|
|
|
return point
|
|
|
|
}
|
|
|
|
|
2019-12-09 18:15:28 +01:00
|
|
|
/**
|
2019-12-11 15:29:59 +01:00
|
|
|
* Clipping describes the
|
|
|
|
* piece of the map that is shown. The returned object contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
|
|
|
|
*
|
|
|
|
* E.g. if we just show a map of
|
2019-12-09 18:15:28 +01:00
|
|
|
* europe, then we have to set the clipping properly, otherwise
|
|
|
|
* the preojection would produce the wrong results when transforming
|
|
|
|
* from a point to coordinates or the other way around.
|
|
|
|
*
|
|
|
|
* @readonly
|
2019-12-11 15:29:59 +01:00
|
|
|
* @member {object}
|
2019-12-11 16:45:26 +01:00
|
|
|
* @memberof MapProjection
|
2019-12-09 18:15:28 +01:00
|
|
|
*/
|
2019-11-04 10:59:08 +01:00
|
|
|
get clip() {
|
2019-11-25 18:04:11 +01:00
|
|
|
let unclipped = {
|
|
|
|
min: { x: 0, y: 0 },
|
|
|
|
max: { x: 1, y: 1 }
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.opts.clip ? this.opts.clip : unclipped
|
2019-11-04 10:59:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-12-11 16:45:26 +01:00
|
|
|
* Returns the biggest viewport the map projection allows.
|
2019-12-09 18:15:28 +01:00
|
|
|
* This is determined by the projecton or the clipping on the mapapp.
|
2019-11-04 10:59:08 +01:00
|
|
|
*
|
2019-12-09 18:15:28 +01:00
|
|
|
* @readonly
|
2019-12-11 16:45:26 +01:00
|
|
|
* @memberof MapProjection
|
2019-11-04 10:59:08 +01:00
|
|
|
*/
|
|
|
|
get maxViewport() {
|
|
|
|
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 18:15:28 +01:00
|
|
|
/**
|
2019-12-11 16:45:26 +01:00
|
|
|
* Special map projection for DeepZoomMap objects.
|
2019-12-11 15:29:59 +01:00
|
|
|
*
|
2019-12-09 18:15:28 +01:00
|
|
|
* Note: It just transform the clipping parameter of the tiles config
|
|
|
|
* to the clipping of the mapapp.
|
|
|
|
*
|
|
|
|
* @export
|
2019-12-11 16:45:26 +01:00
|
|
|
* @class DeepZoomMapProjection
|
|
|
|
* @extends {MapProjection}
|
2019-12-09 18:15:28 +01:00
|
|
|
*/
|
2019-12-11 16:45:26 +01:00
|
|
|
export class DeepZoomMapProjection extends MapProjection {
|
2019-11-04 10:59:08 +01:00
|
|
|
constructor(projection, tilesConfig, opts = {}) {
|
|
|
|
if (tilesConfig.clip) {
|
|
|
|
opts.clip = {
|
|
|
|
min: {
|
|
|
|
x: tilesConfig.clip.bounds.min[0],
|
|
|
|
y: tilesConfig.clip.bounds.min[1]
|
|
|
|
},
|
|
|
|
max: {
|
|
|
|
x: tilesConfig.clip.bounds.max[0],
|
|
|
|
y: tilesConfig.clip.bounds.max[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
super(projection, opts)
|
|
|
|
this.app = opts.app
|
|
|
|
}
|
|
|
|
}
|