diff --git a/debug/projection.html b/debug/projection.html new file mode 100644 index 00000000000..1c32776bf7c --- /dev/null +++ b/debug/projection.html @@ -0,0 +1,171 @@ + + + + Mapbox GL JS debug page + + + + + + + + +
+
+
+
+
+ +
+ + + + + + + + + + + diff --git a/js/geo/transform.js b/js/geo/transform.js index c5f3989b5d3..16a6abaae6b 100644 --- a/js/geo/transform.js +++ b/js/geo/transform.js @@ -3,6 +3,7 @@ var LngLat = require('./lng_lat'), Point = require('point-geometry'), Coordinate = require('./coordinate'), + util = require('../util/util'), wrap = require('../util/util').wrap, interp = require('../util/interpolate'), TileCoord = require('../source/tile_coord'), @@ -23,7 +24,7 @@ module.exports = Transform; * @param {number} maxZoom * @private */ -function Transform(minZoom, maxZoom) { +function Transform(minZoom, maxZoom, projection) { this.tileSize = 512; // constant this._minZoom = minZoom || 0; @@ -31,6 +32,14 @@ function Transform(minZoom, maxZoom) { this.latRange = [-85.05113, 85.05113]; + if (projection) { + util.extend(this, util.pick(projection, [ + 'project', + 'unproject', + 'latRange' + ])); + } + this.width = 0; this.height = 0; this._center = new LngLat(0, 0); @@ -142,52 +151,26 @@ Transform.prototype = { scaleZoom: function(scale) { return Math.log(scale) / Math.LN2; }, project: function(lnglat, worldSize) { - return new Point( - this.lngX(lnglat.lng, worldSize || this.worldSize), - this.latY(lnglat.lat, worldSize || this.worldSize)); + worldSize = worldSize || this.worldSize; + var x = (180 + lnglat.lng) * worldSize / 360; + var y = 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lnglat.lat * Math.PI / 360)); + y = (180 - y) * worldSize / 360; + return new Point(x, y); }, unproject: function(point, worldSize) { - return new LngLat( - this.xLng(point.x, worldSize || this.worldSize), - this.yLat(point.y, worldSize || this.worldSize)); + worldSize = worldSize || this.worldSize; + var lng = point.x * 360 / worldSize - 180; + var y2 = 180 - point.y * 360 / worldSize; + var lat = 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; + return new LngLat(lng, lat); }, - get x() { return this.lngX(this.center.lng, this.worldSize); }, - get y() { return this.latY(this.center.lat, this.worldSize); }, - - get point() { return new Point(this.x, this.y); }, + get x () { return this.point.x; }, + get y () { return this.point.y; }, - /** - * latitude to absolute x coord - * @param {number} lon - * @param {number} worldSize - * @returns {number} pixel coordinate - * @private - */ - lngX: function(lng, worldSize) { - return (180 + lng) * worldSize / 360; - }, - - /** - * latitude to absolute y coord - * @param {number} lat - * @param {number} worldSize - * @returns {number} pixel coordinate - * @private - */ - latY: function(lat, worldSize) { - var y = 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)); - return (180 - y) * worldSize / 360; - }, - - xLng: function(x, worldSize) { - return x * 360 / worldSize - 180; - }, - - yLat: function(y, worldSize) { - var y2 = 180 - y * 360 / worldSize; - return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; + get point() { + return this.project(this.center); }, panBy: function(offset) { @@ -234,11 +217,11 @@ Transform.prototype = { */ locationCoordinate: function(lnglat) { var k = this.zoomScale(this.tileZoom) / this.worldSize, - ll = LngLat.convert(lnglat); + pt = this.project(LngLat.convert(lnglat)); return new Coordinate( - this.lngX(ll.lng, this.worldSize) * k, - this.latY(ll.lat, this.worldSize) * k, + pt.x * k, + pt.y * k, this.tileZoom); }, @@ -250,9 +233,7 @@ Transform.prototype = { */ coordinateLocation: function(coord) { var worldSize = this.zoomScale(coord.zoom) || this.worldSize; - return new LngLat( - this.xLng(coord.column, worldSize), - this.yLat(coord.row, worldSize)); + return this.unproject(new Point(coord.column, coord.row), worldSize); }, pointCoordinate: function(p) { @@ -329,6 +310,7 @@ Transform.prototype = { _constrain: function() { if (!this.center || !this.width || !this.height || this._constraining) return; + if (!this.latRange && !this.lngRange) return; this._constraining = true; @@ -336,15 +318,25 @@ Transform.prototype = { size = this.size, unmodified = this._unmodified; + var ul = this.project(LngLat.convert([ + this.lngRange ? this.lngRange[0] : 0, + this.latRange ? this.latRange[1] : 0 + ])); + + var lr = this.project(LngLat.convert([ + this.lngRange ? this.lngRange[1] : 0, + this.latRange ? this.latRange[0] : 0 + ])); + if (this.latRange) { - minY = this.latY(this.latRange[1], this.worldSize); - maxY = this.latY(this.latRange[0], this.worldSize); + minY = ul.y; + maxY = lr.y; sy = maxY - minY < size.y ? size.y / (maxY - minY) : 0; } if (this.lngRange) { - minX = this.lngX(this.lngRange[0], this.worldSize); - maxX = this.lngX(this.lngRange[1], this.worldSize); + minX = ul.x; + maxX = lr.x; sx = maxX - minX < size.x ? size.x / (maxX - minX) : 0; } diff --git a/js/ui/map.js b/js/ui/map.js index 5949d10dd9f..69ba6a7096e 100755 --- a/js/ui/map.js +++ b/js/ui/map.js @@ -129,7 +129,7 @@ var Map = module.exports = function(options) { } this.animationLoop = new AnimationLoop(); - this.transform = new Transform(options.minZoom, options.maxZoom); + this.transform = new Transform(options.minZoom, options.maxZoom, options.projection); if (options.maxBounds) { this.setMaxBounds(options.maxBounds);