diff --git a/docs/documentation.yml b/docs/documentation.yml index 51038ea2ea8..2f69418b898 100644 --- a/docs/documentation.yml +++ b/docs/documentation.yml @@ -19,6 +19,7 @@ toc: - LngLatBoundsLike - Point - PointLike + - MercatorCoordinate - name: User Interface description: | Controls, markers, and popups add new user interface elements to the map. diff --git a/docs/pages/example/custom-style-layer.html b/docs/pages/example/custom-style-layer.html index 3cbc1ddc796..8f2172ebca6 100644 --- a/docs/pages/example/custom-style-layer.html +++ b/docs/pages/example/custom-style-layer.html @@ -2,8 +2,8 @@ diff --git a/src/geo/coordinate.js b/src/geo/coordinate.js deleted file mode 100644 index 52f981d77ab..00000000000 --- a/src/geo/coordinate.js +++ /dev/null @@ -1,79 +0,0 @@ -// @flow - -/** - * A coordinate is a column, row, zoom combination, often used - * as the data component of a tile. - * - * @param {number} column - * @param {number} row - * @param {number} zoom - * @private - */ -class Coordinate { - column: number; - row: number; - zoom: number; - constructor(column: number, row: number, zoom: number) { - this.column = column; - this.row = row; - this.zoom = zoom; - } - - /** - * Create a clone of this coordinate that can be mutated without - * changing the original coordinate - * - * @returns {Coordinate} clone - * @private - * var coord = new Coordinate(0, 0, 0); - * var c2 = coord.clone(); - * // since coord is cloned, modifying a property of c2 does - * // not modify it. - * c2.zoom = 2; - */ - clone() { - return new Coordinate(this.column, this.row, this.zoom); - } - - /** - * Zoom this coordinate to a given zoom level. This returns a new - * coordinate object, not mutating the old one. - * - * @param {number} zoom - * @returns {Coordinate} zoomed coordinate - * @private - * @example - * var coord = new Coordinate(0, 0, 0); - * var c2 = coord.zoomTo(1); - * c2 // equals new Coordinate(0, 0, 1); - */ - zoomTo(zoom: number) { return this.clone()._zoomTo(zoom); } - - /** - * Subtract the column and row values of this coordinate from those - * of another coordinate. The other coordinat will be zoomed to the - * same level as `this` before the subtraction occurs - * - * @param {Coordinate} c other coordinate - * @returns {Coordinate} result - * @private - */ - sub(c: Coordinate) { return this.clone()._sub(c); } - - _zoomTo(zoom: number) { - const scale = Math.pow(2, zoom - this.zoom); - this.column *= scale; - this.row *= scale; - this.zoom = zoom; - return this; - } - - _sub(c: Coordinate) { - c = c.zoomTo(this.zoom); - this.column -= c.column; - this.row -= c.row; - return this; - } -} - -export default Coordinate; diff --git a/src/geo/mercator_coordinate.js b/src/geo/mercator_coordinate.js new file mode 100644 index 00000000000..30d18bd7820 --- /dev/null +++ b/src/geo/mercator_coordinate.js @@ -0,0 +1,118 @@ +// @flow + +import LngLat from '../geo/lng_lat'; +import type {LngLatLike} from '../geo/lng_lat'; + +/* + * The circumference of the world in meters at the given latitude. + */ +function circumferenceAtLatitude(latitude: number) { + const circumference = 2 * Math.PI * 6378137; + return circumference * Math.cos(latitude * Math.PI / 180); +} + +export function mercatorXfromLng(lng: number) { + return (180 + lng) / 360; +} + +export function mercatorYfromLat(lat: number) { + return (180 - (180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)))) / 360; +} + +export function mercatorZfromAltitude(altitude: number, lat: number) { + return altitude / circumferenceAtLatitude(lat); +} + +export function lngFromMercatorX(x: number) { + return x * 360 - 180; +} + +export function latFromMercatorY(y: number) { + const y2 = 180 - y * 360; + return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; +} + +export function altitudeFromMercatorZ(z: number, y: number) { + return z * circumferenceAtLatitude(latFromMercatorY(y)); +} + +/** + * A `MercatorCoordinate` object represents a projected three dimensional position. + * + * `MercatorCoordinate` uses the web mercator projection ([EPSG:3857](https://epsg.io/3857)) with slightly different units: + * - the size of 1 unit is the width of the projected world instead of the "mercator meter" + * - the origin of the coordinate space is at the north-west corner instead of the middle + * + * For example, `MercatorCoordinate(0, 0, 0)` is the north-west corner of the mercator world and + * `MercatorCoordinate(1, 1, 0)` is the south-east corner. If you are familiar with + * [vector tiles](https://github.com/mapbox/vector-tile-spec) it may be helpful to think + * of the coordinate space as the `0/0/0` tile with an extent of `1`. + * + * The `z` dimension of `MercatorCoordinate` is conformal. A cube in the mercator coordinate space would be rendered as a cube. + * + * @param {number} x The x component of the position. + * @param {number} y The y component of the position. + * @param {number} z The z component of the position. + * @example + * var nullIsland = new mapboxgl.MercatorCoordinate(0.5, 0.5, 0); + * + * @see [Add a custom style layer](https://www.mapbox.com/mapbox-gl-js/example/custom-style-layer/) + */ +class MercatorCoordinate { + x: number; + y: number; + z: number; + + constructor(x: number, y: number, z: number = 0) { + this.x = +x; + this.y = +y; + this.z = +z; + } + + /** + * Project a `LngLat` to a `MercatorCoordinate`. + * + * @param {LngLatLike} lngLatLike The location to project. + * @param {number} altitude The altitude in meters of the position. + * @returns {MercatorCoordinate} The projected mercator coordinate. + * @example + * var coord = mapboxgl.MercatorCoordinate.fromLngLat({ lng: 0, lat: 0}, 0); + * coord; // MercatorCoordinate(0.5, 0.5, 0) + */ + static fromLngLat(lngLatLike: LngLatLike, altitude: number = 0) { + const lngLat = LngLat.convert(lngLatLike); + + return new MercatorCoordinate( + mercatorXfromLng(lngLat.lng), + mercatorYfromLat(lngLat.lat), + mercatorZfromAltitude(altitude, lngLat.lat)); + } + + /** + * Returns the `LatLng` for the coordinate. + * + * @returns {LngLat} The `LngLat` object. + * @example + * var coord = new mapboxgl.MercatorCoordinate(0.5, 0.5, 0); + * var latLng = coord.toLatLng(); // LngLat(0, 0) + */ + toLngLat() { + return new LngLat( + lngFromMercatorX(this.x), + latFromMercatorY(this.y)); + } + + /** + * Returns the altitude in meters of the coordinate. + * + * @returns {number} The altitude in meters. + * @example + * var coord = new mapboxgl.MercatorCoordinate(0, 0, 0.02); + * coord.toAltitude(); // 6914.281956295339 + */ + toAltitude() { + return altitudeFromMercatorZ(this.z, this.y); + } +} + +export default MercatorCoordinate; diff --git a/src/geo/transform.js b/src/geo/transform.js index acda6fc631b..3445e4e93c6 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -2,8 +2,8 @@ import LngLat from './lng_lat'; import LngLatBounds from './lng_lat_bounds'; +import MercatorCoordinate, {mercatorXfromLng, mercatorYfromLat, mercatorZfromAltitude} from './mercator_coordinate'; import Point from '@mapbox/point-geometry'; -import Coordinate from './coordinate'; import { wrap, clamp } from '../util/util'; import {number as interpolate} from '../style-spec/util/interpolate'; import tileCover from '../util/tile_cover'; @@ -203,12 +203,12 @@ class Transform { getVisibleUnwrappedCoordinates(tileID: CanonicalTileID) { const result = [new UnwrappedTileID(0, tileID)]; if (this._renderWorldCopies) { - const utl = this.pointCoordinate(new Point(0, 0), 0); - const utr = this.pointCoordinate(new Point(this.width, 0), 0); - const ubl = this.pointCoordinate(new Point(this.width, this.height), 0); - const ubr = this.pointCoordinate(new Point(0, this.height), 0); - const w0 = Math.floor(Math.min(utl.column, utr.column, ubl.column, ubr.column)); - const w1 = Math.floor(Math.max(utl.column, utr.column, ubl.column, ubr.column)); + const utl = this.pointCoordinate(new Point(0, 0)); + const utr = this.pointCoordinate(new Point(this.width, 0)); + const ubl = this.pointCoordinate(new Point(this.width, this.height)); + const ubr = this.pointCoordinate(new Point(0, this.height)); + const w0 = Math.floor(Math.min(utl.x, utr.x, ubl.x, ubr.x)); + const w1 = Math.floor(Math.max(utl.x, utr.x, ubl.x, ubr.x)); // Add an extra copy of the world on each side to properly render ImageSources and CanvasSources. // Both sources draw outside the tile boundaries of the tile that "contains them" so we need @@ -251,13 +251,14 @@ class Transform { if (options.minzoom !== undefined && z < options.minzoom) return []; if (options.maxzoom !== undefined && z > options.maxzoom) z = options.maxzoom; - const centerCoord = this.pointCoordinate(this.centerPoint, z); - const centerPoint = new Point(centerCoord.column - 0.5, centerCoord.row - 0.5); + const centerCoord = MercatorCoordinate.fromLngLat(this.center); + const numTiles = Math.pow(2, z); + const centerPoint = new Point(numTiles * centerCoord.x - 0.5, numTiles * centerCoord.y - 0.5); const cornerCoords = [ - this.pointCoordinate(new Point(0, 0), z), - this.pointCoordinate(new Point(this.width, 0), z), - this.pointCoordinate(new Point(this.width, this.height), z), - this.pointCoordinate(new Point(0, this.height), z) + this.pointCoordinate(new Point(0, 0)), + this.pointCoordinate(new Point(this.width, 0)), + this.pointCoordinate(new Point(this.width, this.height)), + this.pointCoordinate(new Point(0, this.height)) ]; return tileCover(z, cornerCoords, options.reparseOverscaled ? actualZ : z, this._renderWorldCopies) .sort((a, b) => centerPoint.dist(a.canonical) - centerPoint.dist(b.canonical)); @@ -278,50 +279,26 @@ class Transform { scaleZoom(scale: number) { return Math.log(scale) / Math.LN2; } project(lnglat: LngLat) { + const lat = clamp(lnglat.lat, -this.maxValidLatitude, this.maxValidLatitude); return new Point( - this.lngX(lnglat.lng), - this.latY(lnglat.lat)); + mercatorXfromLng(lnglat.lng) * this.worldSize, + mercatorYfromLat(lat) * this.worldSize); } unproject(point: Point): LngLat { - return new LngLat( - this.xLng(point.x), - this.yLat(point.y)); + return new MercatorCoordinate(point.x / this.worldSize, point.y / this.worldSize).toLngLat(); } - get x(): number { return this.lngX(this.center.lng); } - get y(): number { return this.latY(this.center.lat); } - - get point(): Point { return new Point(this.x, this.y); } - - /** - * longitude to absolute x coord - * @returns {number} pixel coordinate - */ - lngX(lng: number) { - return (180 + lng) * this.worldSize / 360; - } - /** - * latitude to absolute y coord - * @returns {number} pixel coordinate - */ - latY(lat: number) { - lat = clamp(lat, -this.maxValidLatitude, this.maxValidLatitude); - const y = 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)); - return (180 - y) * this.worldSize / 360; - } - - xLng(x: number) { - return x * 360 / this.worldSize - 180; - } - yLat(y: number) { - const y2 = 180 - y * 360 / this.worldSize; - return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; - } + get point(): Point { return this.project(this.center); } setLocationAtPoint(lnglat: LngLat, point: Point) { - const translate = this.pointCoordinate(point)._sub(this.pointCoordinate(this.centerPoint)); - this.center = this.coordinateLocation(this.locationCoordinate(lnglat)._sub(translate)); + const a = this.pointCoordinate(point); + const b = this.pointCoordinate(this.centerPoint); + const loc = this.locationCoordinate(lnglat); + const newCenter = new MercatorCoordinate( + loc.x - (a.x - b.x), + loc.y - (a.y - b.y)); + this.center = this.coordinateLocation(newCenter); if (this._renderWorldCopies) { this.center = this.center.wrap(); } @@ -352,10 +329,7 @@ class Transform { * @returns {Coordinate} */ locationCoordinate(lnglat: LngLat) { - return new Coordinate( - this.lngX(lnglat.lng) / this.tileSize, - this.latY(lnglat.lat) / this.tileSize, - this.zoom).zoomTo(this.tileZoom); + return MercatorCoordinate.fromLngLat(lnglat); } /** @@ -363,16 +337,11 @@ class Transform { * @param {Coordinate} coord * @returns {LngLat} lnglat */ - coordinateLocation(coord: Coordinate) { - const zoomedCoord = coord.zoomTo(this.zoom); - return new LngLat( - this.xLng(zoomedCoord.column * this.tileSize), - this.yLat(zoomedCoord.row * this.tileSize)); + coordinateLocation(coord: MercatorCoordinate) { + return coord.toLngLat(); } - pointCoordinate(p: Point, zoom?: number) { - if (zoom === undefined) zoom = this.tileZoom; - + pointCoordinate(p: Point) { const targetZ = 0; // since we don't know the correct projected z value for the point, // unproject two points to get a line and then find the point on that @@ -395,10 +364,9 @@ class Transform { const t = z0 === z1 ? 0 : (targetZ - z0) / (z1 - z0); - return new Coordinate( - interpolate(x0, x1, t) / this.tileSize, - interpolate(y0, y1, t) / this.tileSize, - this.zoom)._zoomTo(zoom); + return new MercatorCoordinate( + interpolate(x0, x1, t) / this.worldSize, + interpolate(y0, y1, t) / this.worldSize); } /** @@ -406,9 +374,8 @@ class Transform { * @param {Coordinate} coord * @returns {Point} screen point */ - coordinatePoint(coord: Coordinate) { - const zoomedCoord = coord.zoomTo(this.zoom); - const p = [zoomedCoord.column * this.tileSize, zoomedCoord.row * this.tileSize, 0, 1]; + coordinatePoint(coord: MercatorCoordinate) { + const p = [coord.x * this.worldSize, coord.y * this.worldSize, 0, 1]; vec4.transformMat4(p, p, this.pixelMatrix); return new Point(p[0] / p[3], p[1] / p[3]); } @@ -492,25 +459,27 @@ class Transform { if (this.latRange) { const latRange = this.latRange; - minY = this.latY(latRange[1]); - maxY = this.latY(latRange[0]); + minY = mercatorYfromLat(latRange[1]) * this.worldSize; + maxY = mercatorYfromLat(latRange[0]) * this.worldSize; sy = maxY - minY < size.y ? size.y / (maxY - minY) : 0; } if (this.lngRange) { const lngRange = this.lngRange; - minX = this.lngX(lngRange[0]); - maxX = this.lngX(lngRange[1]); + minX = mercatorXfromLng(lngRange[0]) * this.worldSize; + maxX = mercatorXfromLng(lngRange[1]) * this.worldSize; sx = maxX - minX < size.x ? size.x / (maxX - minX) : 0; } + const point = this.point; + // how much the map should scale to fit the screen into given latitude/longitude ranges const s = Math.max(sx || 0, sy || 0); if (s) { this.center = this.unproject(new Point( - sx ? (maxX + minX) / 2 : this.x, - sy ? (maxY + minY) / 2 : this.y)); + sx ? (maxX + minX) / 2 : point.x, + sy ? (maxY + minY) / 2 : point.y)); this.zoom += this.scaleZoom(s); this._unmodified = unmodified; this._constraining = false; @@ -518,7 +487,7 @@ class Transform { } if (this.latRange) { - const y = this.y, + const y = point.y, h2 = size.y / 2; if (y - h2 < minY) y2 = minY + h2; @@ -526,7 +495,7 @@ class Transform { } if (this.lngRange) { - const x = this.x, + const x = point.x, w2 = size.x / 2; if (x - w2 < minX) x2 = minX + w2; @@ -536,8 +505,8 @@ class Transform { // pan the map if the screen goes off the range if (x2 !== undefined || y2 !== undefined) { this.center = this.unproject(new Point( - x2 !== undefined ? x2 : this.x, - y2 !== undefined ? y2 : this.y)); + x2 !== undefined ? x2 : point.x, + y2 !== undefined ? y2 : point.y)); } this._unmodified = unmodified; @@ -556,7 +525,8 @@ class Transform { const halfFov = this._fov / 2; const groundAngle = Math.PI / 2 + this._pitch; const topHalfSurfaceDistance = Math.sin(halfFov) * this.cameraToCenterDistance / Math.sin(Math.PI - groundAngle - halfFov); - const x = this.x, y = this.y; + const point = this.point; + const x = point.x, y = point.y; // Calculate z distance of the farthest fragment that should be rendered. const furthestDistance = Math.cos(Math.PI / 2 - this._pitch) * topHalfSurfaceDistance + this.cameraToCenterDistance; @@ -578,9 +548,7 @@ class Transform { this.mercatorMatrix = mat4.scale([], m, [this.worldSize, this.worldSize, this.worldSize]); // scale vertically to meters per pixel (inverse of ground resolution): - // worldSize / (circumferenceOfEarth * cos(lat * π / 180)) - const verticalScale = this.worldSize / (2 * Math.PI * 6378137 * Math.abs(Math.cos(this.center.lat * (Math.PI / 180)))); - mat4.scale(m, m, [1, 1, verticalScale, 1]); + mat4.scale(m, m, [1, 1, mercatorZfromAltitude(1, this.center.lat) * this.worldSize, 1]); this.projMatrix = m; @@ -617,8 +585,8 @@ class Transform { // calcMatrices hasn't run yet if (!this.pixelMatrixInverse) return 1; - const coord = this.pointCoordinate(new Point(0, 0)).zoomTo(this.zoom); - const p = [coord.column * this.tileSize, coord.row * this.tileSize, 0, 1]; + const coord = this.pointCoordinate(new Point(0, 0)); + const p = [coord.x * this.worldSize, coord.y * this.worldSize, 0, 1]; const topPoint = vec4.transformMat4(p, p, this.pixelMatrix); return topPoint[3] / this.cameraToCenterDistance; } diff --git a/src/index.js b/src/index.js index ca505cd65f6..d04047eb8e0 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,7 @@ import Style from './style/style'; import LngLat from './geo/lng_lat'; import LngLatBounds from './geo/lng_lat_bounds'; import Point from '@mapbox/point-geometry'; +import MercatorCoordinate from './geo/mercator_coordinate'; import {Evented} from './util/evented'; import config from './util/config'; import {setRTLTextPlugin} from './source/rtl_text_plugin'; @@ -37,6 +38,7 @@ const exported = { LngLat, LngLatBounds, Point, + MercatorCoordinate, Evented, config, diff --git a/src/render/program/hillshade_program.js b/src/render/program/hillshade_program.js index e84c9626427..ddfa8948da1 100644 --- a/src/render/program/hillshade_program.js +++ b/src/render/program/hillshade_program.js @@ -11,7 +11,7 @@ import { UniformMatrix4f } from '../uniform_binding'; import EXTENT from '../../data/extent'; -import Coordinate from '../../geo/coordinate'; +import MercatorCoordinate from '../../geo/mercator_coordinate'; import type Context from '../../gl/context'; import type {UniformValues, UniformLocations} from '../uniform_binding'; @@ -104,13 +104,11 @@ const hillshadeUniformPrepareValues = ( function getTileLatRange(painter: Painter, tileID: OverscaledTileID) { // for scaling the magnitude of a points slope by its latitude - const coordinate0 = tileID.toCoordinate(); - const coordinate1 = new Coordinate( - coordinate0.column, coordinate0.row + 1, coordinate0.zoom); + const tilesAtZoom = Math.pow(2, tileID.canonical.z); + const y = tileID.canonical.y; return [ - painter.transform.coordinateLocation(coordinate0).lat, - painter.transform.coordinateLocation(coordinate1).lat - ]; + new MercatorCoordinate(0, y / tilesAtZoom).toLngLat().lat, + new MercatorCoordinate(0, (y + 1) / tilesAtZoom).toLngLat().lat]; } export { diff --git a/src/source/image_source.js b/src/source/image_source.js index 8b7d7ddc323..ce3013f454f 100644 --- a/src/source/image_source.js +++ b/src/source/image_source.js @@ -1,10 +1,6 @@ // @flow -import { getCoordinatesCenter } from '../util/util'; - import { CanonicalTileID } from './tile_id'; -import LngLat from '../geo/lng_lat'; -import Point from '@mapbox/point-geometry'; import { Event, ErrorEvent, Evented } from '../util/evented'; import { getImage, ResourceType } from '../util/ajax'; import browser from '../util/browser'; @@ -13,13 +9,13 @@ import { RasterBoundsArray } from '../data/array_types'; import rasterBoundsAttributes from '../data/raster_bounds_attributes'; import SegmentVector from '../data/segment'; import Texture from '../render/texture'; +import MercatorCoordinate from '../geo/mercator_coordinate'; import type {Source} from './source'; import type {CanvasSourceSpecification} from './canvas_source'; import type Map from '../ui/map'; import type Dispatcher from '../util/dispatcher'; import type Tile from './tile'; -import type Coordinate from '../geo/coordinate'; import type {Callback} from '../types/callback'; import type VertexBuffer from '../gl/vertex_buffer'; import type { @@ -84,7 +80,6 @@ class ImageSource extends Evented implements Source { map: Map; texture: Texture | null; image: ImageData; - centerCoord: Coordinate; tileID: CanonicalTileID; _boundsArray: RasterBoundsArray; boundsBuffer: VertexBuffer; @@ -180,35 +175,21 @@ class ImageSource extends Evented implements Source { // and create a buffer with the corner coordinates. These coordinates // may be outside the tile, because raster tiles aren't clipped when rendering. - const map = this.map; - // transform the geo coordinates into (zoom 0) tile space coordinates - const cornerZ0Coords = coordinates.map((coord) => { - return map.transform.locationCoordinate(LngLat.convert(coord)).zoomTo(0); - }); + const cornerCoords = coordinates.map(MercatorCoordinate.fromLngLat); // Compute the coordinates of the tile we'll use to hold this image's // render data - const centerCoord = this.centerCoord = getCoordinatesCenter(cornerZ0Coords); - // `column` and `row` may be fractional; round them down so that they - // represent integer tile coordinates - centerCoord.column = Math.floor(centerCoord.column); - centerCoord.row = Math.floor(centerCoord.row); - this.tileID = new CanonicalTileID(centerCoord.zoom, centerCoord.column, centerCoord.row); + this.tileID = getCoordinatesCenterTileID(cornerCoords); // Constrain min/max zoom to our tile's zoom level in order to force // SourceCache to request this tile (no matter what the map's zoom // level) - this.minzoom = this.maxzoom = centerCoord.zoom; + this.minzoom = this.maxzoom = this.tileID.z; // Transform the corner coordinates into the coordinate space of our // tile. - const tileCoords = cornerZ0Coords.map((coord) => { - const zoomedCoord = coord.zoomTo(centerCoord.zoom); - return new Point( - Math.round((zoomedCoord.column - centerCoord.column) * EXTENT), - Math.round((zoomedCoord.row - centerCoord.row) * EXTENT)); - }); + const tileCoords = cornerCoords.map((coord) => this.tileID.getTilePoint(coord)._round()); this._boundsArray = new RasterBoundsArray(); this._boundsArray.emplaceBack(tileCoords[0].x, tileCoords[0].y, 0, 0); @@ -285,4 +266,35 @@ class ImageSource extends Evented implements Source { } } +/** + * Given a list of coordinates, get their center as a coordinate. + * + * @returns centerpoint + * @private + */ +export function getCoordinatesCenterTileID(coords: Array) { + let minX = Infinity; + let minY = Infinity; + let maxX = -Infinity; + let maxY = -Infinity; + + for (const coord of coords) { + minX = Math.min(minX, coord.x); + minY = Math.min(minY, coord.y); + maxX = Math.max(maxX, coord.x); + maxY = Math.max(maxY, coord.y); + } + + const dx = maxX - minX; + const dy = maxY - minY; + const dMax = Math.max(dx, dy); + const zoom = Math.max(0, Math.floor(-Math.log(dMax) / Math.LN2)); + const tilesAtZoom = Math.pow(2, zoom); + + return new CanonicalTileID( + zoom, + Math.floor((minX + maxX) / 2 * tilesAtZoom), + Math.floor((minY + maxY) / 2 * tilesAtZoom)); +} + export default ImageSource; diff --git a/src/source/query_features.js b/src/source/query_features.js index 078c5c65a91..ffa9eb18475 100644 --- a/src/source/query_features.js +++ b/src/source/query_features.js @@ -2,7 +2,7 @@ import type SourceCache from './source_cache'; import type StyleLayer from '../style/style_layer'; -import type Coordinate from '../geo/coordinate'; +import type MercatorCoordinate from '../geo/mercator_coordinate'; import type CollisionIndex from '../symbol/collision_index'; import type Transform from '../geo/transform'; import type { RetainedQueryData } from '../symbol/placement'; @@ -11,7 +11,7 @@ import assert from 'assert'; export function queryRenderedFeatures(sourceCache: SourceCache, styleLayers: {[string]: StyleLayer}, - queryGeometry: Array, + queryGeometry: Array, params: { filter: FilterSpecification, layers: Array }, transform: Transform) { const maxPitchScaleFactor = transform.maxPitchScaleFactor(); diff --git a/src/source/source_cache.js b/src/source/source_cache.js index 0976507b204..f80a059d8b0 100644 --- a/src/source/source_cache.js +++ b/src/source/source_cache.js @@ -5,7 +5,7 @@ import { create as createSource } from './source'; import Tile from './tile'; import { Event, ErrorEvent, Evented } from '../util/evented'; import TileCache from './tile_cache'; -import Coordinate from '../geo/coordinate'; +import MercatorCoordinate from '../geo/mercator_coordinate'; import { keysDifference } from '../util/util'; import EXTENT from '../data/extent'; import Context from '../gl/context'; @@ -734,7 +734,7 @@ class SourceCache extends Evented { * @param queryGeometry coordinates of the corners of bounding rectangle * @returns {Array} result items have {tile, minX, maxX, minY, maxY}, where min/max bounding values are the given bounds transformed in into the coordinate space of this tile. */ - tilesIn(queryGeometry: Array, maxPitchScaleFactor: number) { + tilesIn(queryGeometry: Array, maxPitchScaleFactor: number) { const tileResults = []; const ids = this.getIds(); @@ -742,14 +742,13 @@ class SourceCache extends Evented { let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity; - const z = queryGeometry[0].zoom; for (let k = 0; k < queryGeometry.length; k++) { const p = queryGeometry[k]; - minX = Math.min(minX, p.column); - minY = Math.min(minY, p.row); - maxX = Math.max(maxX, p.column); - maxY = Math.max(maxY, p.row); + minX = Math.min(minX, p.x); + minY = Math.min(minY, p.y); + maxX = Math.max(maxX, p.x); + maxY = Math.max(maxY, p.y); } @@ -764,8 +763,8 @@ class SourceCache extends Evented { const queryPadding = maxPitchScaleFactor * tile.queryPadding * EXTENT / tile.tileSize / scale; const tileSpaceBounds = [ - coordinateToTilePoint(tileID, new Coordinate(minX, minY, z)), - coordinateToTilePoint(tileID, new Coordinate(maxX, maxY, z)) + tileID.getTilePoint(new MercatorCoordinate(minX, minY)), + tileID.getTilePoint(new MercatorCoordinate(maxX, maxY)) ]; if (tileSpaceBounds[0].x - queryPadding < EXTENT && tileSpaceBounds[0].y - queryPadding < EXTENT && @@ -773,7 +772,7 @@ class SourceCache extends Evented { const tileSpaceQueryGeometry = []; for (let j = 0; j < queryGeometry.length; j++) { - tileSpaceQueryGeometry.push(coordinateToTilePoint(tileID, queryGeometry[j])); + tileSpaceQueryGeometry.push(tileID.getTilePoint(queryGeometry[j])); } tileResults.push({ @@ -835,18 +834,6 @@ class SourceCache extends Evented { SourceCache.maxOverzooming = 10; SourceCache.maxUnderzooming = 3; -/** - * Convert a coordinate to a point in a tile's coordinate space. - * @private - */ -function coordinateToTilePoint(tileID: OverscaledTileID, coord: Coordinate): Point { - const zoomedCoord = coord.zoomTo(tileID.canonical.z); - return new Point( - (zoomedCoord.column - (tileID.canonical.x + tileID.wrap * Math.pow(2, tileID.canonical.z))) * EXTENT, - (zoomedCoord.row - tileID.canonical.y) * EXTENT - ); -} - function compareKeyZoom(a, b) { return ((a % 32) - (b % 32)) || (b - a); } diff --git a/src/source/tile_id.js b/src/source/tile_id.js index d763ea1df10..d6430920495 100644 --- a/src/source/tile_id.js +++ b/src/source/tile_id.js @@ -1,10 +1,12 @@ // @flow import {getTileBBox} from '@mapbox/whoots-js'; +import EXTENT from '../data/extent'; +import Point from '@mapbox/point-geometry'; +import MercatorCoordinate from '../geo/mercator_coordinate'; import assert from 'assert'; import { register } from '../util/web_worker_transfer'; -import Coordinate from '../geo/coordinate'; export class CanonicalTileID { z: number; @@ -39,6 +41,13 @@ export class CanonicalTileID { .replace('{quadkey}', quadkey) .replace('{bbox-epsg-3857}', bbox); } + + getTilePoint(coord: MercatorCoordinate) { + const tilesAtZoom = Math.pow(2, this.z); + return new Point( + (coord.x * tilesAtZoom - this.x) * EXTENT, + (coord.y * tilesAtZoom - this.y) * EXTENT); + } } export class UnwrappedTileID { @@ -142,8 +151,8 @@ export class OverscaledTileID { return `${this.overscaledZ}/${this.canonical.x}/${this.canonical.y}`; } - toCoordinate() { - return new Coordinate(this.canonical.x + Math.pow(2, this.wrap), this.canonical.y, this.canonical.z); + getTilePoint(coord: MercatorCoordinate) { + return this.canonical.getTilePoint(new MercatorCoordinate(coord.x - this.wrap, coord.y)); } } diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index 3e6f8c46772..402402181c9 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -106,10 +106,11 @@ type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array) => * @name prerender * @param {WebGLRenderingContext} gl The map's gl context. * @param {Array} matrix The map's camera matrix. It projects spherical mercator - * coordinates to gl coordinates. The spherical mercator coordinate `[0, 0]` represents the + * coordinates to gl coordinates. The mercator coordinate `[0, 0]` represents the * top left corner of the mercator world and `[1, 1]` represents the bottom right corner. When * the `renderingMode` is `"3d"`, the z coordinate is conformal. A box with identical x, y, and z - * lengths in mercator units would be rendered as a cube. + * lengths in mercator units would be rendered as a cube. {@link MercatorCoordinate}.fromLatLng + * can be used to project a `LngLat` to a mercator coordinate. */ /** @@ -131,7 +132,8 @@ type CustomRenderMethod = (gl: WebGLRenderingContext, matrix: Array) => * coordinates to gl coordinates. The spherical mercator coordinate `[0, 0]` represents the * top left corner of the mercator world and `[1, 1]` represents the bottom right corner. When * the `renderingMode` is `"3d"`, the z coordinate is conformal. A box with identical x, y, and z - * lengths in mercator units would be rendered as a cube. + * lengths in mercator units would be rendered as a cube. {@link MercatorCoordinate}.fromLatLng + * can be used to project a `LngLat` to a mercator coordinate. */ export type CustomLayerInterface = { id: string, diff --git a/src/util/tile_cover.js b/src/util/tile_cover.js index ce715afeae3..408ecd907b3 100644 --- a/src/util/tile_cover.js +++ b/src/util/tile_cover.js @@ -1,12 +1,13 @@ // @flow -import Coordinate from '../geo/coordinate'; +import MercatorCoordinate from '../geo/mercator_coordinate'; +import Point from '@mapbox/point-geometry'; import { OverscaledTileID } from '../source/tile_id'; export default tileCover; -function tileCover(z: number, bounds: [Coordinate, Coordinate, Coordinate, Coordinate], +function tileCover(z: number, bounds: [MercatorCoordinate, MercatorCoordinate, MercatorCoordinate, MercatorCoordinate], actualZ: number, renderWorldCopies: boolean | void): Array { if (renderWorldCopies === undefined) { renderWorldCopies = true; @@ -28,12 +29,14 @@ function tileCover(z: number, bounds: [Coordinate, Coordinate, Coordinate, Coord } } + const zoomedBounds = bounds.map((coord) => new Point(coord.x, coord.y)._mult(tiles)); + // Divide the screen up in two triangles and scan each of them: // +---/ // | / | // /---+ - scanTriangle(bounds[0], bounds[1], bounds[2], 0, tiles, scanLine); - scanTriangle(bounds[2], bounds[3], bounds[0], 0, tiles, scanLine); + scanTriangle(zoomedBounds[0], zoomedBounds[1], zoomedBounds[2], 0, tiles, scanLine); + scanTriangle(zoomedBounds[2], zoomedBounds[3], zoomedBounds[0], 0, tiles, scanLine); return Object.keys(t).map((id) => { return t[id]; @@ -44,15 +47,15 @@ function tileCover(z: number, bounds: [Coordinate, Coordinate, Coordinate, Coord // Taken from polymaps src/Layer.js // https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383 -function edge(a: Coordinate, b: Coordinate) { - if (a.row > b.row) { const t = a; a = b; b = t; } +function edge(a: Point, b: Point) { + if (a.y > b.y) { const t = a; a = b; b = t; } return { - x0: a.column, - y0: a.row, - x1: b.column, - y1: b.row, - dx: b.column - a.column, - dy: b.row - a.row + x0: a.x, + y0: a.y, + x1: b.x, + y1: b.y, + dx: b.x - a.x, + dy: b.y - a.y }; } @@ -79,7 +82,7 @@ function scanSpans(e0, e1, ymin, ymax, scanLine) { } } -function scanTriangle(a: Coordinate, b: Coordinate, c: Coordinate, ymin, ymax, scanLine) { +function scanTriangle(a: Point, b: Point, c: Point, ymin, ymax, scanLine) { let ab = edge(a, b), bc = edge(b, c), ca = edge(c, a); diff --git a/src/util/util.js b/src/util/util.js index ed67934fa60..ee2f99276d6 100644 --- a/src/util/util.js +++ b/src/util/util.js @@ -2,7 +2,6 @@ import UnitBezier from '@mapbox/unitbezier'; -import Coordinate from '../geo/coordinate'; import Point from '@mapbox/point-geometry'; import window from './window'; @@ -248,33 +247,6 @@ export function bindAll(fns: Array, context: Object): void { }); } -/** - * Given a list of coordinates, get their center as a coordinate. - * - * @returns centerpoint - * @private - */ -export function getCoordinatesCenter(coords: Array): Coordinate { - let minX = Infinity; - let minY = Infinity; - let maxX = -Infinity; - let maxY = -Infinity; - - for (let i = 0; i < coords.length; i++) { - minX = Math.min(minX, coords[i].column); - minY = Math.min(minY, coords[i].row); - maxX = Math.max(maxX, coords[i].column); - maxY = Math.max(maxY, coords[i].row); - } - - const dx = maxX - minX; - const dy = maxY - minY; - const dMax = Math.max(dx, dy); - const zoom = Math.max(0, Math.floor(-Math.log(dMax) / Math.LN2)); - return new Coordinate((minX + maxX) / 2, (minY + maxY) / 2, 0) - .zoomTo(zoom); -} - /** * Determine if a string ends with a particular substring * diff --git a/test/node_modules/mapbox-gl-js-test/fixed.js b/test/node_modules/mapbox-gl-js-test/fixed.js index 1f2eff9620a..388144cf405 100644 --- a/test/node_modules/mapbox-gl-js-test/fixed.js +++ b/test/node_modules/mapbox-gl-js-test/fixed.js @@ -22,8 +22,8 @@ function fixedLngLat(l, precision) { function fixedCoord(coord, precision) { if (precision === undefined) precision = 10; return { - column: fixedNum(coord.column, precision), - row: fixedNum(coord.row, precision), - zoom: coord.zoom + x: fixedNum(coord.x, precision), + y: fixedNum(coord.y, precision), + z: fixedNum(coord.z, precision) }; } diff --git a/test/unit/geo/coordinate.test.js b/test/unit/geo/coordinate.test.js deleted file mode 100644 index 944cb41a52b..00000000000 --- a/test/unit/geo/coordinate.test.js +++ /dev/null @@ -1,46 +0,0 @@ -import { test } from 'mapbox-gl-js-test'; -import Coordinate from '../../../src/geo/coordinate'; - -test('Coordinate', (t) => { - t.test('#constructor', (t) => { - const c = new Coordinate(1, 2, 3); - t.equal(c.column, 1); - t.equal(c.row, 2); - t.equal(c.zoom, 3); - t.end(); - }); - - t.test('#zoomTo', (t) => { - let c = new Coordinate(1, 2, 3); - c = c.zoomTo(3); - t.equal(c.column, 1); - t.equal(c.row, 2); - t.equal(c.zoom, 3); - c = c.zoomTo(2); - t.equal(c.column, 0.5); - t.equal(c.row, 1); - t.equal(c.zoom, 2); - c = c.zoomTo(5); - t.equal(c.column, 4); - t.equal(c.row, 8); - t.equal(c.zoom, 5); - t.end(); - }); - - t.test('#sub', (t) => { - const o = new Coordinate(5, 4, 3); - const c = new Coordinate(1, 2, 3); - const r = o.sub(c); - t.equal(r.column, 4); - t.equal(r.row, 2); - t.equal(r.zoom, 3); - const otherZoom = new Coordinate(4, 4, 4); - const r2 = o.sub(otherZoom); - t.equal(r2.column, 3); - t.equal(r2.row, 2); - t.equal(r2.zoom, 3); - t.end(); - }); - - t.end(); -}); diff --git a/test/unit/geo/mercator_coordinate.test.js b/test/unit/geo/mercator_coordinate.test.js new file mode 100644 index 00000000000..be16014deec --- /dev/null +++ b/test/unit/geo/mercator_coordinate.test.js @@ -0,0 +1,31 @@ +import { test } from 'mapbox-gl-js-test'; +import LngLat from '../../../src/geo/lng_lat'; +import MercatorCoordinate from '../../../src/geo/mercator_coordinate'; + +test('LngLat', (t) => { + t.test('#constructor', (t) => { + t.ok(new MercatorCoordinate(0, 0) instanceof MercatorCoordinate, 'creates an object'); + t.ok(new MercatorCoordinate(0, 0, 0) instanceof MercatorCoordinate, 'creates an object with altitude'); + t.end(); + }); + + t.test('#fromLngLat', (t) => { + const nullIsland = new LngLat(0, 0); + t.deepEqual(MercatorCoordinate.fromLngLat(nullIsland), { x: 0.5, y: 0.5, z: 0 }); + t.end(); + }); + + t.test('#toLngLat', (t) => { + const dc = new LngLat(-77, 39); + t.deepEqual(MercatorCoordinate.fromLngLat(dc, 500).toLngLat(), { lng: -77, lat: 39 }); + t.end(); + }); + + t.test('#toAltitude', (t) => { + const dc = new LngLat(-77, 39); + t.equal(MercatorCoordinate.fromLngLat(dc, 500).toAltitude(), 500); + t.end(); + }); + + t.end(); +}); diff --git a/test/unit/geo/transform.test.js b/test/unit/geo/transform.test.js index c6d24a9bc79..45cd538f0fb 100644 --- a/test/unit/geo/transform.test.js +++ b/test/unit/geo/transform.test.js @@ -33,13 +33,11 @@ test('transform', (t) => { t.equal(transform.scaleZoom(0), -Infinity); t.equal(transform.scaleZoom(10), 3.3219280948873626); t.deepEqual(transform.point, new Point(262144, 262144)); - t.equal(transform.x, 262144); - t.equal(transform.y, 262144); t.equal(transform.height, 500); t.deepEqual(fixedLngLat(transform.pointLocation(new Point(250, 250))), { lng: 0, lat: 0 }); - t.deepEqual(fixedCoord(transform.pointCoordinate(new Point(250, 250))), { column: 512, row: 512, zoom: 10 }); + t.deepEqual(fixedCoord(transform.pointCoordinate(new Point(250, 250))), { x: 0.5, y: 0.5, z: 0 }); t.deepEqual(transform.locationPoint(new LngLat(0, 0)), { x: 250, y: 250 }); - t.deepEqual(transform.locationCoordinate(new LngLat(0, 0)), { column: 512, row: 512, zoom: 10 }); + t.deepEqual(transform.locationCoordinate(new LngLat(0, 0)), { x: 0.5, y: 0.5, z: 0 }); t.end(); }); @@ -101,7 +99,7 @@ test('transform', (t) => { t.equal(transform.zoom, 5.135709286104402); transform.center = new LngLat(-50, -30); - t.same(transform.center, new LngLat(0, -0.006358305286099153)); + t.same(transform.center, new LngLat(0, -0.0063583052861417855)); transform.zoom = 10; transform.center = new LngLat(-50, -30); @@ -220,8 +218,8 @@ test('transform', (t) => { t.test('clamps latitude', (t) => { const transform = new Transform(); - t.equal(transform.latY(-90), transform.latY(-transform.maxValidLatitude)); - t.equal(transform.latY(90), transform.latY(transform.maxValidLatitude)); + t.deepEqual(transform.project(new LngLat(0, -90)), transform.project(new LngLat(0, -transform.maxValidLatitude))); + t.deepEqual(transform.project(new LngLat(0, 90)), transform.project(new LngLat(0, transform.maxValidLatitude))); t.end(); }); diff --git a/test/unit/source/source_cache.test.js b/test/unit/source/source_cache.test.js index 8d927427d1c..ff0f43e87b6 100644 --- a/test/unit/source/source_cache.test.js +++ b/test/unit/source/source_cache.test.js @@ -5,10 +5,10 @@ import Tile from '../../../src/source/tile'; import { OverscaledTileID } from '../../../src/source/tile_id'; import Transform from '../../../src/geo/transform'; import LngLat from '../../../src/geo/lng_lat'; -import Coordinate from '../../../src/geo/coordinate'; import { Event, ErrorEvent, Evented } from '../../../src/util/evented'; import { extend } from '../../../src/util/util'; import browser from '../../../src/util/browser'; +import MercatorCoordinate from '../../../src/geo/mercator_coordinate'; // Add a mocked source type for use in these tests function MockSourceType(id, sourceOptions, _dispatcher, eventedParent) { @@ -1231,8 +1231,8 @@ test('SourceCache#tilesIn', (t) => { const sourceCache = createSourceCache({ noLoad: true }); sourceCache.onAdd(); t.same(sourceCache.tilesIn([ - new Coordinate(0.5, 0.25, 1), - new Coordinate(1.5, 0.75, 1) + new MercatorCoordinate(0.25, 0.125), + new MercatorCoordinate(0.75, 0.375) ]), []); t.end(); @@ -1263,8 +1263,8 @@ test('SourceCache#tilesIn', (t) => { ]); const tiles = sourceCache.tilesIn([ - new Coordinate(0.5, 0.25, 1), - new Coordinate(1.5, 0.75, 1) + new MercatorCoordinate(0.25, 0.125), + new MercatorCoordinate(0.75, 0.375) ], 1); tiles.sort((a, b) => { return a.tile.tileID.canonical.x - b.tile.tileID.canonical.x; }); @@ -1314,8 +1314,8 @@ test('SourceCache#tilesIn', (t) => { ]); const tiles = sourceCache.tilesIn([ - new Coordinate(0.5, 0.25, 1), - new Coordinate(1.5, 0.75, 1) + new MercatorCoordinate(0.25, 0.125), + new MercatorCoordinate(0.75, 0.375) ], 1); tiles.sort((a, b) => { return a.tile.tileID.canonical.x - b.tile.tileID.canonical.x; }); diff --git a/test/unit/util/tile_cover.test.js b/test/unit/util/tile_cover.test.js index c788f4976a3..6b6d5e3ea94 100644 --- a/test/unit/util/tile_cover.test.js +++ b/test/unit/util/tile_cover.test.js @@ -1,5 +1,6 @@ import { test } from 'mapbox-gl-js-test'; import tileCover from '../../../src/util/tile_cover'; +import MercatorCoordinate from '../../../src/geo/mercator_coordinate'; import { OverscaledTileID } from '../../../src/source/tile_id'; test('tileCover', (t) => { @@ -8,10 +9,10 @@ test('tileCover', (t) => { t.test('calculates tile coverage at w = 0', (t) => { const z = 2, coords = [ - {column: 0, row: 1, zoom: 2}, - {column: 1, row: 1, zoom: 2}, - {column: 1, row: 2, zoom: 2}, - {column: 0, row: 2, zoom: 2} + new MercatorCoordinate(0, 0.25), + new MercatorCoordinate(0.25, 0.25), + new MercatorCoordinate(0.25, 0.5), + new MercatorCoordinate(0, 0.5) ], res = tileCover(z, coords, z); t.deepEqual(res, [new OverscaledTileID(2, 0, 2, 0, 1)]); @@ -21,10 +22,10 @@ test('tileCover', (t) => { t.test('calculates tile coverage at w > 0', (t) => { const z = 2, coords = [ - {column: 12, row: 1, zoom: 2}, - {column: 13, row: 1, zoom: 2}, - {column: 13, row: 2, zoom: 2}, - {column: 12, row: 2, zoom: 2} + new MercatorCoordinate(3, 0.25), + new MercatorCoordinate(3.25, 0.25), + new MercatorCoordinate(3.25, 0.5), + new MercatorCoordinate(3, 0.5) ], res = tileCover(z, coords, z); t.deepEqual(res, [new OverscaledTileID(2, 3, 2, 0, 1)]); @@ -34,10 +35,10 @@ test('tileCover', (t) => { t.test('calculates tile coverage at w = -1', (t) => { const z = 2, coords = [ - {column: -1, row: 1, zoom: 2}, - {column: 0, row: 1, zoom: 2}, - {column: 0, row: 2, zoom: 2}, - {column: -1, row: 2, zoom: 2} + new MercatorCoordinate(-0.25, 0.25), + new MercatorCoordinate(0, 0.25), + new MercatorCoordinate(0, 0.5), + new MercatorCoordinate(-0.25, 0.5) ], res = tileCover(z, coords, z); t.deepEqual(res, [new OverscaledTileID(2, -1, 2, 3, 1)]); @@ -47,10 +48,10 @@ test('tileCover', (t) => { t.test('calculates tile coverage at w < -1', (t) => { const z = 2, coords = [ - {column: -13, row: 1, zoom: 2}, - {column: -12, row: 1, zoom: 2}, - {column: -12, row: 2, zoom: 2}, - {column: -13, row: 2, zoom: 2} + new MercatorCoordinate(-3.25, 0.25), + new MercatorCoordinate(-3, 0.25), + new MercatorCoordinate(-3, 0.5), + new MercatorCoordinate(-3.25, 0.5) ], res = tileCover(z, coords, z); t.deepEqual(res, [new OverscaledTileID(2, -4, 2, 3, 1)]); @@ -60,10 +61,10 @@ test('tileCover', (t) => { t.test('calculates tile coverage across meridian', (t) => { const z = 2, coords = [ - {column: -0.5, row: 1, zoom: 2}, - {column: 0.5, row: 1, zoom: 2}, - {column: 0.5, row: 2, zoom: 2}, - {column: -0.5, row: 2, zoom: 2} + new MercatorCoordinate(-0.125, 0.25), + new MercatorCoordinate(0.125, 0.25), + new MercatorCoordinate(0.125, 0.5), + new MercatorCoordinate(-0.125, 0.5) ], res = tileCover(z, coords, z); t.deepEqual(res, [ @@ -75,10 +76,10 @@ test('tileCover', (t) => { t.test('only includes tiles for a single world, if renderWorldCopies is set to false', (t) => { const z = 2, coords = [ - {column: -0.5, row: 1, zoom: 2}, - {column: 0.5, row: 1, zoom: 2}, - {column: 0.5, row: 2, zoom: 2}, - {column: -0.5, row: 2, zoom: 2} + new MercatorCoordinate(-0.125, 0.25), + new MercatorCoordinate(0.125, 0.25), + new MercatorCoordinate(0.125, 0.5), + new MercatorCoordinate(-0.125, 0.5) ], renderWorldCopies = false, res = tileCover(z, coords, z, renderWorldCopies); diff --git a/test/unit/util/util.test.js b/test/unit/util/util.test.js index 5b4ddf6f931..d0b2481da9f 100644 --- a/test/unit/util/util.test.js +++ b/test/unit/util/util.test.js @@ -2,8 +2,7 @@ import { test } from 'mapbox-gl-js-test'; -import Coordinate from '../../../src/geo/coordinate'; -import { easeCubicInOut, keysDifference, extend, pick, uniqueId, getCoordinatesCenter, bindAll, asyncAll, clamp, wrap, bezier, endsWith, mapObject, filterObject, deepEqual, clone, arraysIntersect, isCounterClockwise, isClosedPolygon, parseCacheControl, uuid, validateUuid } from '../../../src/util/util'; +import { easeCubicInOut, keysDifference, extend, pick, uniqueId, bindAll, asyncAll, clamp, wrap, bezier, endsWith, mapObject, filterObject, deepEqual, clone, arraysIntersect, isCounterClockwise, isClosedPolygon, parseCacheControl, uuid, validateUuid } from '../../../src/util/util'; import Point from '@mapbox/point-geometry'; test('util', (t) => { @@ -18,14 +17,6 @@ test('util', (t) => { t.deepEqual(pick({a:1, b:2, c:3}, ['a', 'c', 'd']), {a:1, c:3}, 'pick'); t.ok(typeof uniqueId() === 'number', 'uniqueId'); - t.test('getCoordinatesCenter', (t) => { - t.deepEqual(getCoordinatesCenter([ - new Coordinate(0, 0, 2), - new Coordinate(1, 1, 2) - ]), new Coordinate(0.5, 0.5, 0)); - t.end(); - }); - t.test('bindAll', (t) => { function MyClass() { bindAll(['ontimer'], this);