Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions packages/maptalks/src/layer/tile/TileLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,12 +534,11 @@ class TileLayer extends Layer {
if (!offsets[node.z + 1]) {
offsets[node.z + 1] = this._getTileOffset(node.z + 1);
}
this._splitNode(node, projectionView, queue, tiles, extent, maxZoom, offsets[node.z + 1], parentRenderer, glRes);
if (this.isParentTile(z, maxZoom, node)) {
parents.push(node);
}
this._splitNode(node, projectionView, queue, tiles, parents, z, extent, maxZoom, offsets[node.z + 1], parentRenderer, glRes);
}
parents.sort(sortingTiles);
this._debugTile(tiles, '_getPyramidTiles');

return {
tileGrids: [
{
Expand All @@ -555,11 +554,10 @@ class TileLayer extends Layer {
} as TilesType;
}

isParentTile(z: number, maxZoom: number, tile: TileNodeType) {
const stackMinZoom = Math.max(this.getMinZoom(), z - this.options['tileStackStartDepth']);
const stackMaxZoom = Math.min(maxZoom, stackMinZoom + this.options['tileStackDepth']);
return tile.z >= stackMinZoom && tile.z < stackMaxZoom;

isParentTile(currentTileZoom: number, maxZoom: number, tile: TileNodeType) {
const stackMinZoom = Math.max(this.getMinZoom(), currentTileZoom - this.options['tileStackStartDepth']);
const stackMaxZoom = stackMinZoom + this.options['tileStackDepth'];
return tile.z >= stackMinZoom && tile.z < stackMaxZoom && tile.z < maxZoom;
}

//@internal
Expand All @@ -568,12 +566,15 @@ class TileLayer extends Layer {
projectionView: Matrix4,
queue: TileNodeType[],
tiles: TileNodeType[],
parents: TileNodeType[],
currentTileZoom: number,
gridExtent: PointExtent,
maxZoom: number,
offset: TileOffsetType,
parentRenderer: any,
glRes: number
) {

const z = node.z + 1;
const sr = this._spatialRef || this.getSpatialReference();
const { idx, idy } = node;
Expand Down Expand Up @@ -614,11 +615,11 @@ class TileLayer extends Layer {
childNode.offset[0] = offset[0];
childNode.offset[1] = offset[1];
const visible = this._isTileVisible(childNode, projectionView, glScale, maxZoom, offset);
if (visible === 1) {
if (visible === TileVisibility.VISIBLE) {
hasCurrentIn = true;
} else if (visible === -1) {
} else if (visible === TileVisibility.OUT_OF_FRUSTUM) {
continue;
} else if (visible === 0 && z !== maxZoom) {
} else if (visible === TileVisibility.SCREEN_ERROR_TOO_SMALL && z !== maxZoom) {
// 任意子瓦片的error低于maxError,则添加父级瓦片,不再遍历子瓦片
tiles.push(node);
gridExtent._combine(node.extent2d);
Expand All @@ -636,6 +637,9 @@ class TileLayer extends Layer {
} else {
pushIn(queue, children);
}
if (this.isParentTile(currentTileZoom, maxZoom, node)) {
parents.push(node);
}

}

Expand Down Expand Up @@ -677,18 +681,18 @@ class TileLayer extends Layer {
//@internal
_isTileVisible(node: TileNodeType, projectionView: Matrix4, glScale: number, maxZoom: number, offset: TileOffsetType) {
if (node.z === 0) {
return 1;
return TileVisibility.VISIBLE;
}
if (!this._isTileInFrustum(node, projectionView, glScale, offset)/* || this._isTileTooSmall(node, projectionView, glScale, maxZoom, offset)*/) {
return -1;
return TileVisibility.OUT_OF_FRUSTUM;
}
let maxError = this.options['maxError'];
if (isNil(maxError)) {
maxError = DEFAULT_MAXERROR;
}
const error = this._getScreenSpaceError(node, glScale, maxZoom, offset);

return error >= maxError ? 1 : 0;
return error >= maxError ? TileVisibility.VISIBLE : TileVisibility.SCREEN_ERROR_TOO_SMALL;
}

// _isTileTooSmall(node, projectionView, glScale, maxZoom, offset) {
Expand Down Expand Up @@ -1603,6 +1607,32 @@ class TileLayer extends Layer {
}
return bboxInMask(tileBBOX, this._maskGeoJSON);
}

//@internal
_debugTile(tile: TileNodeType | TileNodeType[], name?: string, debugOn?: boolean) {
if (this.options['debugTile']) {
if (Array.isArray(tile)) {
for (let i = 0; i < tile.length; i++) {
if (!tile[i]) {
continue;
}
this._debugTile(tile[i], name + ' at ' + i, debugOn);
}
return;
}
if (!tile) {
return;
}
const { x, y, z } = this.options['debugTile'];
if (tile.x === x && tile.y === y && tile.z === z) {
console.log(`Debug Tile Found in TileLayer.${name}:`, tile);
if (debugOn) {
// eslint-disable-next-line no-debugger
debugger
}
}
}
}
}

TileLayer.registerJSONType('TileLayer');
Expand Down Expand Up @@ -1723,3 +1753,9 @@ export type TileLayerOptionsType = LayerOptionsType & {
currentTilesFirst?: boolean;
tileErrorScale?: number;
};

enum TileVisibility {
OUT_OF_FRUSTUM,
SCREEN_ERROR_TOO_SMALL,
VISIBLE
}
1 change: 0 additions & 1 deletion packages/maptalks/src/map/Map.Anim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ Map.include(/** @lends Map.prototype */{
if (frame.styles['center']) {
const center = frame.styles['center'];
this._setPrjCenter(projection.project(center));
this._centerZ = center.z;
this.onMoving(this._parseEventFromCoord(this.getCenter()));
} else if (frame.styles['prjCenter']) {
const center = frame.styles['prjCenter'];
Expand Down
76 changes: 58 additions & 18 deletions packages/maptalks/src/map/Map.Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,58 @@ Map.include(/** @lends Map.prototype */{
}, frame);
},


/**
* Look at the given coordinates.
* @param {Coordinate} coordinates - coordinates to look at
* @returns {Map} this
*/
lookAt(params) {
let { coordinates, bearing, pitch, distance } = params;
coordinates = params.center || coordinates;
if (Array.isArray(coordinates)) {
coordinates = new Coordinate(coordinates as any);
}
if (!coordinates.z && !distance) {
return this.setCenter(coordinates)
}
const glRes = this.getGLRes();
bearing = isNil(bearing) ? this.getBearing(): bearing;
pitch = isNil(pitch) ? this.getPitch() : pitch;
const radPitch = pitch * RADIAN;
const radBearing = bearing * RADIAN;
if (distance) {
let distZ = distance * Math.sin(radPitch);
let xyDist = Math.sin(radPitch) * distance;
const distX = xyDist * Math.sin(radBearing);
const distY = xyDist * Math.cos(radBearing);
distZ = distZ * this._meterToGLPoint;
xyDist = this.distanceToPointAtRes(distX, distY, glRes);
distance = Math.sqrt(xyDist * xyDist + distZ * distZ);
}
const point = this.coordToPointAtRes(coordinates, glRes);
point.z = 0;
if (coordinates.z) {
point.z = coordinates.z * this._meterToGLPoint;
}

const cameraToTargetDistance = distance || this.cameraToGroundDistance || this.cameraToCenterDistance;
const xyDistance = Math.sin(radPitch) * cameraToTargetDistance;
const cz = cameraToTargetDistance * Math.cos(radPitch);
const cx = point.x - xyDistance * Math.sin(radBearing);
const cy = point.y - xyDistance * Math.cos(radBearing);
const cameraPosition = [cx, cy, cz + point.z];
const cameraPoint = new Point(cameraPosition as Vector3);
const cameraCoord = this.pointAtResToCoord(cameraPoint, glRes);
const cameraAltitude = cameraPoint.z / this._meterToGLPoint;
cameraCoord.z = cameraAltitude;
return this.setCameraOrientation({
position: cameraCoord.toArray(),
bearing,
pitch
});
},

/**
* Set camera's orientation
* @param {Object} options - options
Expand Down Expand Up @@ -400,6 +452,7 @@ Map.include(/** @lends Map.prototype */{
this._angle = -angle(BEARING as any, SOUTH as any);
this._zoomLevel = this.getFitZoomForCamera(coordinate, this._pitch).zoom;
this._calcMatrices();
return this;
},

getFitZoomForCamera(cameraPosition: Array<number> | Coordinate, pitch: number) {
Expand All @@ -426,22 +479,8 @@ Map.include(/** @lends Map.prototype */{

getFitZoomForAltitude(distance: number) {
const ratio = this._getFovRatio();
const scale = distance * ratio * 2 / (this.height || 1) * this.getGLRes();
const resolutions = this._getResolutions();
let z = 0;
for (z; z < resolutions.length - 1; z++) {
if (resolutions[z] === scale) {
return z;
} else if (resolutions[z + 1] === scale) {
return z + 1;
} else if (scale < resolutions[z] && scale > resolutions[z + 1]) {
z = (scale - resolutions[z]) / (resolutions[z + 1] - resolutions[z]) + z;
return z - 1;
} else {
continue;
}
}
return z;
const res = distance * ratio * 2 / (this.height || 1) * this.getGLRes();
return this.getZoomFromRes(res);
},

/**
Expand Down Expand Up @@ -817,7 +856,7 @@ Map.include(/** @lends Map.prototype */{
return function () {
const glRes = this.getGLRes();
if (!this._meterToGLPoint) {
this._meterToGLPoint = this.distanceToPointAtRes(100, 0, glRes).x / 100;
this._meterToGLPoint = this.altitudeToPoint(100, glRes) / 100;
}

const center2D = this._prjToPointAtRes(this._prjCenter, glRes, TEMP_POINT);
Expand All @@ -835,6 +874,7 @@ Map.include(/** @lends Map.prototype */{
const cameraToCenterDistance = this._getFovZ();
const cameraZenithDistance = this.cameraZenithDistance === undefined ? cameraToCenterDistance : this.cameraZenithDistance;
const cameraToGroundDistance = cameraZenithDistance - centerPointZ;
this.cameraToGroundDistance = cameraToGroundDistance;
const cz = cameraToGroundDistance * Math.cos(pitch);
// and [dist] away from map's center on XY plane to tilt the scene.
const dist = Math.sin(pitch) * cameraToGroundDistance;
Expand Down Expand Up @@ -906,7 +946,7 @@ Map.include(/** @lends Map.prototype */{

//@internal
_recenterOnTerrain() {
if (this.centerAltitude === undefined || this._centerZ !== undefined) {
if (this.centerAltitude === undefined) {
return;
}
let queriedAltitude = this._queryTerrainByProjCoord(this._prjCenter);
Expand Down
5 changes: 0 additions & 5 deletions packages/maptalks/src/map/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,6 @@ export class Map extends Handlerable(Eventable(Renderable(Class))) {
//@internal
_center: Coordinate;
//@internal
_centerZ: number;
//@internal
_mapViewPoint: Point;
isMap: boolean;
//@internal
Expand Down Expand Up @@ -353,7 +351,6 @@ export class Map extends Handlerable(Eventable(Renderable(Class))) {

this._zoomLevel = zoom;
this._center = center;
this._centerZ = center.z;

this.setSpatialReference(opts['spatialReference'] || opts['view']);

Expand Down Expand Up @@ -608,7 +605,6 @@ export class Map extends Handlerable(Eventable(Renderable(Class))) {
const center = projection.unproject(this._prjCenter);
center.x = Math.round(center.x * 1E8) / 1E8;
center.y = Math.round(center.y * 1E8) / 1E8;
center.z = this._centerZ;
if (this.centerAltitude) {
center.z = this.centerAltitude;
}
Expand Down Expand Up @@ -642,7 +638,6 @@ export class Map extends Handlerable(Eventable(Renderable(Class))) {
this._center = center;
return this;
}
this._centerZ = center.z;
this.onMoveStart();
this._setPrjCenter(pcenter);
this.onMoveEnd(this._parseEventFromCoord(this.getCenter()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ const TileLayerRenderable = function <T extends MixinConstructor>(Base: T) {

for (let j = 0, l = allTiles.length; j < l; j++) {
const tile = allTiles[j];
layer._debugTile(tile, '_getTilesInCurrentFrame');
const tileId = tile.id;
const isParentTile = !isFirstRender && j < parentCount;
//load tile in cache at first if it has.
Expand Down
44 changes: 43 additions & 1 deletion packages/maptalks/test/map/MapCameraSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,49 @@ describe('Map.Camera', function () {
expect(map.getPitch()).to.be.eql(0);
expect(map.getBearing()).to.be.eql(0);
expect(map.getZoom()).to.be.within(14, 15);
})
});

it('map.lookAt', function () {
const view = map.getView();
const cameraPosition = map.cameraPosition.slice();
const pitch = map.getPitch();
const bearing = map.getBearing();

map.lookAt(view);

expect(map.cameraPosition).to.be.closeTo(cameraPosition);
expect(map.getPitch()).to.be.eql(pitch);
expect(map.getBearing()).to.be.eql(bearing);
});

it('map.lookAt with distance', function () {
const view = map.getView();
let distance = map.cameraToCenterDistance;

// compute meter distance in XYZ against map.cameraToCenterDistance
const glRes = map.getGLRes();
const radBearing = map.getBearing() * Math.PI / 180;
const radPitch = map.getPitch() * Math.PI / 180;
let distZ = distance * Math.sin(radPitch);
let xyDist = Math.sin(radPitch) * distance;
const distX = xyDist * Math.sin(radBearing);
const distY = xyDist * Math.cos(radBearing);
distZ = distZ / map._meterToGLPoint;
xyDist = map.pointAtResToDistance(distX, distY, glRes);
distance = Math.sqrt(xyDist * xyDist + distZ * distZ);

const cameraPosition = map.cameraPosition.slice();
const pitch = map.getPitch();
const bearing = map.getBearing();

view.distance = map.pointAtResToAltitude(distance, map.getGLRes());
map.lookAt(view);

expect(map.cameraPosition).to.be.closeTo(cameraPosition);
expect(map.cameraPosition[2]).to.be.eql(cameraPosition[2]);
expect(map.getPitch()).to.be.eql(pitch);
expect(map.getBearing()).to.be.eql(bearing);
});
});

describe('containsPoint when pitching', function () {
Expand Down