Skip to content

Commit 005ec9d

Browse files
authored
map's center support altitude (#2217)
1 parent fc320b4 commit 005ec9d

File tree

14 files changed

+9264
-9167
lines changed

14 files changed

+9264
-9167
lines changed

docs/api/0.x/renderer.CanvasRenderer.html

Lines changed: 4586 additions & 4568 deletions
Large diffs are not rendered by default.

docs/api/1.x/renderer.CanvasRenderer.html

Lines changed: 4566 additions & 4548 deletions
Large diffs are not rendered by default.

src/core/util/vec3.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,16 @@ function hypot() {
162162
while (i--) y += arguments[i] * arguments[i];
163163
return Math.sqrt(y);
164164
}
165+
166+
export function angle(a, b) {
167+
normalize(a, a);
168+
normalize(b, b);
169+
const cosine = dot(a, b);
170+
if (cosine > 1.0) {
171+
return 0;
172+
} else if (cosine < -1.0) {
173+
return Math.PI;
174+
} else {
175+
return Math.acos(cosine);
176+
}
177+
}

src/map/Map.Camera.js

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Map from './Map';
22
import Point from '../geo/Point';
33
import Coordinate from '../geo/Coordinate';
44
import * as mat4 from '../core/util/mat4';
5-
import { subtract, add, scale, normalize, dot, set, distance } from '../core/util/vec3';
5+
import { subtract, add, scale, normalize, dot, set, distance, angle } from '../core/util/vec3';
66
import { clamp, interpolate, isNumber, isNil, wrap, toDegree, toRadian } from '../core/util';
77
import { applyMatrix, matrixToQuaternion, quaternionToMatrix, lookAt, setPosition } from '../core/util/math';
88
import Browser from '../core/Browser';
@@ -11,6 +11,7 @@ const RADIAN = Math.PI / 180;
1111
const DEFAULT_FOV = 0.6435011087932844;
1212
const TEMP_COORD = new Coordinate(0, 0);
1313
const TEMP_POINT = new Point(0, 0);
14+
const SOUTH = [0, -1, 0], BEARING = [];
1415

1516
const altitudesHasData = (altitudes) => {
1617
if (isNumber(altitudes)) {
@@ -287,26 +288,10 @@ Map.include(/** @lends Map.prototype */{
287288
* @property {Number} pitch
288289
* @property {Number} bearing
289290
*/
290-
setCameraPosition(params) {
291+
setCameraOrientation(params) {
291292
const { position, pitch, bearing } = params;
292-
293-
const cameraAltitude = position[2] * this._meterToGLPoint;
294-
295-
const centerAltitude = this.centerAltitude || 0;
296-
const centerPointZ = centerAltitude * this._meterToGLPoint;
297-
298-
const cz = cameraAltitude - centerPointZ;
299-
300-
const pitchRadian = pitch * RADIAN;
301-
302-
const cameraToGroundDistance = cz / Math.cos(pitchRadian);
303-
304-
const dist = Math.sin(pitchRadian) * cameraToGroundDistance;
305-
306-
const cameraToCenterDistance = cameraToGroundDistance + centerPointZ;
307-
308-
const zoom = this._getZoomFromCameraToCenterDistance(cameraToCenterDistance);
309-
293+
const { zoom, cameraToGroundDistance } = this.getFitZoomForCamera(position, pitch);
294+
const dist = Math.sin(pitch * RADIAN) * cameraToGroundDistance;
310295
const wrapBearing = wrap(bearing, -180, 180);
311296
const bearingRadian = wrapBearing * RADIAN;
312297

@@ -329,7 +314,43 @@ Map.include(/** @lends Map.prototype */{
329314
return this;
330315
},
331316

332-
_getZoomFromCameraToCenterDistance(distance) {
317+
//设置相机的坐标, 根据地图中心点和相机位置,反算地图的bearing、pitch、zoom
318+
setCameraPosition(coordinate) {
319+
const glRes = this.getGLRes();
320+
const cameraPoint = this.coordToPointAtRes(coordinate, glRes);
321+
cameraPoint.z = this.altitudeToPoint(coordinate.z || 0, glRes);
322+
const center = this.getCenter();
323+
const centerPoint = this.coordToPointAtRes(center, glRes);
324+
centerPoint.z = this.altitudeToPoint(center.z, glRes);
325+
const direction = subtract([], cameraPoint.toArray(), centerPoint.toArray());
326+
set(this.cameraUp || [0, 0, 0], 0, 0, 1);
327+
this._pitch = angle(direction, this.cameraUp);
328+
set(BEARING, direction[0], direction[1], 0);
329+
this._angle = -angle(BEARING, SOUTH);
330+
this._zoomLevel = this.getFitZoomForCamera(coordinate, this._pitch).zoom;
331+
this._calcMatrices();
332+
},
333+
334+
getFitZoomForCamera(cameraPosition, pitch) {
335+
const z = Array.isArray(cameraPosition) ? cameraPosition[2] : cameraPosition.z;
336+
const cameraAltitude = z * this._meterToGLPoint;
337+
338+
const centerAltitude = this.centerAltitude || 0;
339+
const centerPointZ = centerAltitude * this._meterToGLPoint;
340+
341+
const cz = cameraAltitude - centerPointZ;
342+
343+
const pitchRadian = pitch * RADIAN;
344+
345+
const cameraToGroundDistance = cz / Math.cos(pitchRadian);
346+
347+
const cameraToCenterDistance = cameraToGroundDistance + centerPointZ;
348+
349+
const zoom = this._getFitZoomForDistance(cameraToCenterDistance);
350+
return { zoom, cameraToGroundDistance };
351+
},
352+
353+
_getFitZoomForDistance(distance) {
333354
const ratio = this._getFovRatio();
334355
const scale = distance * ratio * 2 / (this.height || 1) * this.getGLRes();
335356
const resolutions = this._getResolutions();
@@ -712,7 +733,8 @@ Map.include(/** @lends Map.prototype */{
712733
}
713734

714735
const center2D = this._prjToPointAtRes(this._prjCenter, glRes, TEMP_POINT);
715-
const centerAltitude = this.centerAltitude || 0;
736+
const altitude = this.getCenter().z;
737+
const centerAltitude = altitude !== undefined ? altitude : this.centerAltitude || 0;
716738
const centerPointZ = centerAltitude * this._meterToGLPoint;
717739
this.cameraLookAt = set(this.cameraLookAt || [0, 0, 0], center2D.x, center2D.y, centerPointZ);
718740

@@ -749,7 +771,8 @@ Map.include(/** @lends Map.prototype */{
749771
// let up = new vec3(0,1,0);
750772
// up.rotateZ(target,radians);
751773
const d = dist || 1;
752-
const up = this.cameraUp = set(this.cameraUp || [0, 0, 0], Math.sin(bearing) * d, Math.cos(bearing) * d, 0);
774+
// const up = this.cameraUp = set(this.cameraUp || [0, 0, 0], Math.sin(bearing) * d, Math.cos(bearing) * d, 0);
775+
const up = this.cameraUp = this.getPitch() > 0 ? set(this.cameraUp || [0, 0, 0], 0, 0, 1) : set(this.cameraUp || [0, 0, 0], Math.sin(bearing) * d, Math.cos(bearing) * d, 0);
753776
const m = this.cameraWorldMatrix = this.cameraWorldMatrix || createMat4();
754777
lookAt(m, this.cameraPosition, this.cameraLookAt, up);
755778

@@ -775,7 +798,7 @@ Map.include(/** @lends Map.prototype */{
775798
},
776799

777800
_recenterOnTerrain() {
778-
if (this.centerAltitude === undefined) {
801+
if (this.centerAltitude === undefined || this._centerZ !== undefined) {
779802
return;
780803
}
781804
let queriedAltitude = this._queryTerrainByProjCoord(this._prjCenter);

src/map/Map.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ class Map extends Handlerable(Eventable(Renderable(Class))) {
235235

236236
this._zoomLevel = zoom;
237237
this._center = center;
238+
this._centerZ = center.z;
238239

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

@@ -488,6 +489,7 @@ class Map extends Handlerable(Eventable(Renderable(Class))) {
488489
const center = projection.unproject(this._prjCenter);
489490
center.x = Math.round(center.x * 1E8) / 1E8;
490491
center.y = Math.round(center.y * 1E8) / 1E8;
492+
center.z = this._centerZ;
491493
if (this.centerAltitude) {
492494
center.z = this.centerAltitude;
493495
}
@@ -521,6 +523,7 @@ class Map extends Handlerable(Eventable(Renderable(Class))) {
521523
this._center = center;
522524
return this;
523525
}
526+
this._centerZ = center.z || 0;
524527
this.onMoveStart();
525528
this._setPrjCenter(pcenter);
526529
this.onMoveEnd(this._parseEventFromCoord(this.getCenter()));
@@ -2005,6 +2008,7 @@ class Map extends Handlerable(Eventable(Renderable(Class))) {
20052008
delete this._glRes;
20062009
const projection = this.getProjection();
20072010
this._prjCenter = projection.project(this._center);
2011+
this._prjCenter.z = this._center.z;
20082012
this._calcMatrices();
20092013
const renderer = this._getRenderer();
20102014
if (renderer) {

src/renderer/geometry/Painter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ class Painter extends Class {
594594
return;
595595
}
596596
//Multiplexing offset
597-
this.containerOffset = offset || mapStateCache.offset || map._pointToContainerPoint(renderer.southWest)._add(0, -map.height);
597+
this.containerOffset = offset || mapStateCache.offset || map._pointToContainerPoint(renderer.middleWest)._add(0, -map.height / 2);
598598
this._beforePaint();
599599
const ctx = context || renderer.context;
600600
if (!ctx.isHitTesting) {

src/renderer/layer/CanvasRenderer.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ class CanvasRenderer extends Class {
218218
remove() {
219219
this.onRemove();
220220
delete this._loadingResource;
221-
delete this.southWest;
221+
delete this.middleWest;
222222
delete this.canvas;
223223
delete this.context;
224224
delete this.canvasExtent2D;
@@ -266,7 +266,7 @@ class CanvasRenderer extends Class {
266266
return null;
267267
}
268268
// size = this._extent2D.getSize(),
269-
const containerPoint = map._pointToContainerPoint(this.southWest)._add(0, -map.height);
269+
const containerPoint = map._pointToContainerPoint(this.middleWest)._add(0, -map.height / 2);
270270
return {
271271
'image': this.canvas,
272272
'layer': this.layer,
@@ -392,16 +392,16 @@ class CanvasRenderer extends Class {
392392

393393
/**
394394
* Prepare rendering
395-
* Set necessary properties, like this._renderZoom/ this.canvasExtent2D, this.southWest
395+
* Set necessary properties, like this._renderZoom/ this.canvasExtent2D, this.middleWest
396396
* @private
397397
*/
398398
prepareRender() {
399399
delete this._renderComplete;
400400
const map = this.getMap();
401401
this._renderZoom = map.getZoom();
402402
this.canvasExtent2D = this._extent2D = map._get2DExtent();
403-
//change from northWest to southWest, because northwest's point <=> containerPoint changes when pitch >= 72
404-
this.southWest = map._containerPointToPoint(new Point(0, map.height));
403+
//change from northWest to middleWest, because northwest's point <=> containerPoint changes when pitch >= 72
404+
this.middleWest = map._containerPointToPoint(new Point(0, map.height / 2));
405405
}
406406

407407
/**
@@ -575,10 +575,10 @@ class CanvasRenderer extends Class {
575575
if (!mask) {
576576
return false;
577577
}
578-
const old = this.southWest;
578+
const old = this.middleWest;
579579
const map = this.getMap();
580-
//when clipping, layer's southwest needs to be reset for mask's containerPoint conversion
581-
this.southWest = map._containerPointToPoint(new Point(0, map.height));
580+
//when clipping, layer's middleWest needs to be reset for mask's containerPoint conversion
581+
this.middleWest = map._containerPointToPoint(new Point(0, map.height / 2));
582582
context.save();
583583
const dpr = map.getDevicePixelRatio();
584584
if (dpr !== 1) {
@@ -607,20 +607,20 @@ class CanvasRenderer extends Class {
607607
context.restore();
608608
}
609609
context.clip();
610-
this.southWest = old;
610+
this.middleWest = old;
611611
return true;
612612
}
613613

614614
/**
615615
* Get renderer's current view extent in 2d point
616-
* @return {Object} view.extent, view.maskExtent, view.zoom, view.southWest
616+
* @return {Object} view.extent, view.maskExtent, view.zoom, view.middleWest
617617
*/
618618
getViewExtent() {
619619
return {
620620
'extent': this._extent2D,
621621
'maskExtent': this._maskExtent,
622622
'zoom': this._renderZoom,
623-
'southWest': this.southWest
623+
'middleWest': this.middleWest
624624
};
625625
}
626626

src/renderer/layer/vectorlayer/VectorLayerCanvasRenderer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ class VectorLayerRenderer extends OverlayLayerCanvasRenderer {
401401

402402
_updateMapStateCache() {
403403
const map = this.getMap();
404-
const offset = map._pointToContainerPoint(this.southWest)._add(0, -map.height);
404+
const offset = map._pointToContainerPoint(this.middleWest)._add(0, -map.height / 2);
405405
const resolution = map.getResolution();
406406
const pitch = map.getPitch();
407407
const bearing = map.getBearing();

test/layer/CanvasLayerSpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe('Layer.CanvasLayer', function () {
3232

3333
layer.draw = function (context, view, w, h) {
3434
expect(view.extent.isValid()).to.be.ok();
35-
expect(view.southWest).to.be.ok();
35+
expect(view.middleWest).to.be.ok();
3636
expect(view.zoom).to.be.eql(map.getZoom());
3737
expect(w).to.be.eql(size.width);
3838
expect(h).to.be.eql(size.height);

test/map/MapCameraSpec.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ describe('Map.Camera', function () {
379379

380380
map.setPitch(70);
381381
var size3 = geometry.getSize();
382-
expect(size3.width).to.be.above(size2.width);
382+
expect(size2.width).to.be.above(size3.width);
383383
expect(size3.height).to.be.below(size2.height);
384384
});
385385
});
@@ -455,7 +455,7 @@ describe('Map.Camera', function () {
455455

456456
describe('Set camera position', function () {
457457
it('pitch 0, bearing 0', function () {
458-
map.setCameraPosition({
458+
map.setCameraOrientation({
459459
position: [0, 0, 10000],
460460
pitch: 0,
461461
bearing: 0,
@@ -468,7 +468,7 @@ describe('Map.Camera', function () {
468468
});
469469

470470
it('pitch 45, bearing 45', function () {
471-
map.setCameraPosition({
471+
map.setCameraOrientation({
472472
position: [0, 0, 10000],
473473
pitch: 45,
474474
bearing: 45,
@@ -481,7 +481,7 @@ describe('Map.Camera', function () {
481481
});
482482

483483
it('pitch 45, bearing 135', function () {
484-
map.setCameraPosition({
484+
map.setCameraOrientation({
485485
position: [0, 0, 10000],
486486
pitch: 45,
487487
bearing: 135,
@@ -494,7 +494,7 @@ describe('Map.Camera', function () {
494494
});
495495

496496
it('pitch 45, bearing -45', function () {
497-
map.setCameraPosition({
497+
map.setCameraOrientation({
498498
position: [0, 0, 10000],
499499
pitch: 45,
500500
bearing: -45,
@@ -507,7 +507,7 @@ describe('Map.Camera', function () {
507507
});
508508

509509
it('pitch 45, bearing -135', function () {
510-
map.setCameraPosition({
510+
map.setCameraOrientation({
511511
position: [0, 0, 10000],
512512
pitch: 45,
513513
bearing: -135,
@@ -520,7 +520,7 @@ describe('Map.Camera', function () {
520520
});
521521

522522
it('position z', function () {
523-
map.setCameraPosition({
523+
map.setCameraOrientation({
524524
position: [0, 0, 100],
525525
pitch: 0,
526526
bearing: 0,
@@ -629,5 +629,17 @@ describe('Map.Camera', function () {
629629
// expect(maptalks.Util.join(map.domCssMatrix)).to.be.eql([31.819805153394643, -8.235571585149868, 0.7139488752261732, 0.6830127018922193, 31.819805153394636, 8.23557158514987, -0.7139488752261733, -0.6830127018922194, 0, -43.466662183008076, -0.27054191763364316, -0.25881904510252074, 0, 0, 46.83368719036461, 45].join());
630630
expect(maptalks.Util.join(map.domCssMatrix)).to.be.eql([31.819805153394643,-8.235571585149868,0.693731297039628,0.6830127018922193,31.819805153394636,8.23557158514987,-0.6937312970396281,-0.6830127018922194,0,-43.466662183008076,-0.2628807214860012,-0.25881904510252074,0,0,45.635325850044154,45].join());
631631
});
632+
633+
it('setCameraPosition', function() {
634+
const center = map.getCenter();
635+
const position = new maptalks.Coordinate(center.x + 0.01, center.y + 0.01, 100);
636+
map.setCameraPosition(position);
637+
const zoom = map.getZoom();
638+
const pitch = map.getPitch();
639+
const bearing = map.getBearing();
640+
expect(zoom).to.be.eql(14.899641034986649);
641+
expect(pitch).to.be.eql(85.67228588474566);
642+
expect(bearing).to.be.eql(139.8095157874954);
643+
});
632644
});
633645

test/map/MapSpec.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ describe('Map.Spec', function () {
555555
map._getRenderer()._checkSizeInterval = 10;
556556
// this.timeout(map._getRenderer()._checkSizeInterval * 3);
557557
var center = map.getCenter();
558+
center.z = center.z || 0;
558559
map.once('resize', function (param) {
559560
expect(param).to.be.ok();
560561
//center remains
@@ -918,4 +919,12 @@ describe('Map.Spec', function () {
918919
done();
919920
}, 100);
920921
});
922+
923+
it('map\'s center has altitude', function() {
924+
const center = map.getCenter();
925+
center.z = 100;
926+
map.setCenter(center);
927+
const viewMatrix = map.viewMatrix;
928+
expect(viewMatrix).to.be.eql([-1, 0, 0, -0, 0, 1, 0, 0, 0, 0, -1, 0, 173083.2338488889, -49314.063792176465, 0.0703125, 1]);
929+
});
921930
});

test/map/MapViewHistorySpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe('Map View History', function () {
5757
renderer.callInNextFrame(function () {
5858
map.zoomToPreviousView();
5959
var next = map.zoomToNextView();
60-
expect(next.center).to.be.eql([0, 0]);
60+
expect(next.center).to.be.eql([0, 0, 0]);
6161
expect(next.zoom).to.be.eql(zoom - 1);
6262

6363
expect(map.hasNextView()).not.to.be.ok();

test/map/control/Control.OverviewSpec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ describe('Control.Overview', function () {
7171
var overview = new maptalks.control.Overview();
7272
overview.addTo(map);
7373
map.on('viewchange', function () {
74-
expect(overview._overview.getCenter().toArray()).to.be.eql([0, 0]);
74+
expect(overview._overview.getCenter().toArray()).to.be.eql([0, 0, 0]);
7575
done();
7676
});
7777
map.setCenter([0, 0]);

0 commit comments

Comments
 (0)