Skip to content

Commit

Permalink
fix projectCoords when coordinates antiMeridian (#2341)
Browse files Browse the repository at this point in the history
* fix projectCoords when coordinates antiMeridian

* update spec
  • Loading branch information
deyihu authored Aug 8, 2024
1 parent d37c131 commit 53c72cc
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 182 deletions.
9 changes: 6 additions & 3 deletions src/geo/Extent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Extent {
right?: number;
top?: number;
bottom?: number;
antiMeridian?: boolean;

constructor(p1?: WithNull<ExtentLike>, p?: Projection);
constructor(p1: PositionType, p2: PositionType, p?: Projection);
Expand All @@ -97,7 +98,9 @@ class Extent {
this.projection = args[l - 1];
}
this._dirty = true;
this.antiMeridian = false;
this._initialize(args[0], args[1], args[2], args[3]);

}

_initialize(p1: WithNull<ExtentLike>): void;
Expand Down Expand Up @@ -842,11 +845,11 @@ class Extent {
//FIXME a rare but potential bug:
//An extent may be projected by multiple projection
if (ext._dirty) {
TEMP_COORD6.set(ext.xmax, ext.ymin);
TEMP_COORD7.set(ext.xmin, ext.ymax);
TEMP_COORD6.set(ext.xmin, ext.ymin);
TEMP_COORD7.set(ext.xmax, ext.ymax);
MINMAX[0] = TEMP_COORD6;
MINMAX[1] = TEMP_COORD7;
const minmax = proj.projectCoords(MINMAX);
const minmax = proj.projectCoords(MINMAX, this.antiMeridian);
const min = minmax[0],
max = minmax[1];
ext.pxmin = Math.min(min.x, max.x);
Expand Down
16 changes: 9 additions & 7 deletions src/geo/projection/Projection.EPSG3857.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { extend } from '../../core/util/common';
import { wrap, sign } from '../../core/util/util';
import { sign } from '../../core/util/util';
import Common, { type CommonProjectionType } from './Projection';
import Coordinate from '../Coordinate';
import { WGS84Sphere, type WGS84SphereType } from '../measurer';
Expand All @@ -18,10 +18,10 @@ const EPSG3857Projection = {

project: function (lnglat: Coordinate, out?: Coordinate) {
const rad = this.rad,
metersPerDegree = this.metersPerDegree,
max = this.maxLatitude;
metersPerDegree = this.metersPerDegree,
max = this.maxLatitude;
const lng = lnglat.x,
lat = Math.max(Math.min(max, lnglat.y), -max);
lat = Math.max(Math.min(max, lnglat.y), -max);
let c;
if (lat === 0) {
c = 0;
Expand Down Expand Up @@ -56,8 +56,10 @@ const EPSG3857Projection = {
if (Math.abs(Math.abs(c) - this.maxLatitude) < delta) {
c = sign(c) * this.maxLatitude;
}
const rx = wrap(x, -180, 180);
const ry = wrap(c, -this.maxLatitude, this.maxLatitude);
// const rx = wrap(x, -180, 180);
// const ry = wrap(c, -this.maxLatitude, this.maxLatitude);
const rx = x;
const ry = c;
if (out) {
out.x = rx;
out.y = ry;
Expand All @@ -84,4 +86,4 @@ export type EPSG3857ProjectionType = CommonProjectionType & typeof EPSG3857Proje
* {@inheritDoc projection.Common}
* {@inheritDoc measurer.WGS84Sphere}
*/
export default extend<EPSG3857ProjectionType, CommonProjectionType, typeof EPSG3857Projection, WGS84SphereType>({} as EPSG3857ProjectionType, Common, EPSG3857Projection , WGS84Sphere);
export default extend<EPSG3857ProjectionType, CommonProjectionType, typeof EPSG3857Projection, WGS84SphereType>({} as EPSG3857ProjectionType, Common, EPSG3857Projection, WGS84Sphere);
42 changes: 30 additions & 12 deletions src/geo/projection/Projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,42 @@ const CommonProjection = {
if (Array.isArray(coordinates[0])) {
return coordinates.map(coords => this.projectCoords(coords, antiMeridian));
} else {
const antiMeridianEnable = antiMeridian !== false;
const antiMeridianEnable = antiMeridian;
const circum = this.getCircum();
const extent = this.getSphereExtent(),
sx = extent.sx,
sy = extent.sy;
let wrapX, wrapY;
// const extent = this.getSphereExtent(),
// sx = extent.sx,
// sy = extent.sy;
// let wrapX, wrapY;

const extent = this.getSphereExtent();
const sy = extent.sy;
let wrapY;
let pre = coordinates[0], current, dx, dy, p;
const prj = [this.project(pre)];
for (let i = 1, l = coordinates.length; i < l; i++) {
current = coordinates[i];
dx = current.x - pre.x;
dy = current.y - pre.y;
p = this.project(current);
let isAntiMeridian = false;
if (Math.abs(dx) > 180 && antiMeridianEnable) {
if (wrapX === undefined) {
wrapX = current.x > pre.x;
}
if (wrapX) {
p._add(-circum.x * sign(dx) * sx, 0);
current._add(-360 * sign(dx), 0);
//正轴无限扩展
// [[170, 80], [-170, 80]]
if (dx < 0) {
current.x = 180 + 180 - Math.abs(current.x);
} else {
//负轴无限扩展
// [[-170, 80], [170, 80]]
current.x = -180 - (180 - Math.abs(current.x));
}
isAntiMeridian = true;
// if (wrapX === undefined) {
// wrapX = current.x > pre.x;
// }
// if (wrapX) {
// // p._add(-circum.x * sign(dx) * sx, 0);
// // current._add(-360 * sign(dx), 0);
// }
}
if (Math.abs(dy) > 90 && antiMeridianEnable) {
if (wrapY === undefined) {
Expand All @@ -98,6 +113,9 @@ const CommonProjection = {
current._add(0, -180 * sign(dy));
}
}
if (isAntiMeridian) {
p = this.project(current);
}
pre = current;
prj.push(p);
}
Expand Down Expand Up @@ -184,7 +202,7 @@ const CommonProjection = {
getSphereExtent(): Extent {
if (!this.extent && this.isSphere()) {
const max = this.project(new Coordinate(180, 90)),
min = this.project(new Coordinate(-180, -90));
min = this.project(new Coordinate(-180, -90));
this.extent = new Extent(min, max, this);
this.extent.sx = max.x > min.x ? 1 : -1;
this.extent.sy = max.y > min.y ? 1 : -1;
Expand Down
21 changes: 13 additions & 8 deletions test/geometry/AntiMeridianSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ describe('Geometry.AntiMeridian', function () {
});

it('linestring should draw a long line from [170, 80] to [-170, 80]', function () {
map.setCenter([-170, 80]);
map.setCenter([170, 80]);
var line = new maptalks.LineString([[170, 80], [-170, 80]], getGeoOptions());
layer.addGeometry(line);
expect(layer).not.to.be.painted(-2, 0);
expect(layer).to.be.painted(100, 0);
expect(line.getSize()._round().toArray()).to.be.eql([486, 2]);
// expect(layer).to.be.painted(100, 0);
// expect(line.getSize()._round().toArray()).to.be.eql([486, 2]);
expect(layer).to.be.painted(0, 0);
expect(line.getSize()._round().toArray()).to.be.eql([30, 2]);
});

it('linestring should continue to draw from [180, 80] to [180, -80]', function () {
Expand Down Expand Up @@ -105,17 +107,19 @@ describe('Geometry.AntiMeridian', function () {
var polygon = new maptalks.Polygon([[-170, 80], [170, 80], [170, 70]], getGeoOptions());
layer.addGeometry(polygon);
expect(layer).not.to.be.painted(4, 0);
expect(layer).to.be.painted(-10, 0);
// expect(layer).to.be.painted(-10, 0);
expect(layer).to.be.painted(-1, 1);
expect(polygon.getSize()._round().toArray()).to.be.eql([30, 59]);
});

it('polygon should draw a big polygon from [170, 80] to [-170, 70]', function () {
map.setCenter([-170, 80]);
map.setCenter([170, 80]);
var polygon = new maptalks.Polygon([[170, 80], [-170, 70], [-170, 80]], getGeoOptions());
layer.addGeometry(polygon);
expect(layer).not.to.be.painted(-2, 0);
expect(layer).to.be.painted(100, 0);
expect(polygon.getSize()._round().toArray()).to.be.eql([486, 59]);
expect(layer).to.be.painted(1, 1);
// expect(polygon.getSize()._round().toArray()).to.be.eql([486, 59]);
expect(polygon.getSize()._round().toArray()).to.be.eql([30, 59]);
});

it('polygon should continue to draw from [180, 80] to [180, -80]', function () {
Expand Down Expand Up @@ -219,7 +223,8 @@ describe('Geometry.AntiMeridian', function () {
expect(layer).to.be.painted(10, 0);
expect(layer).to.be.painted(0, 10);
expect(layer).not.to.be.painted(0, -10);
expect(rectangle.getSize()._round().toArray()).to.be.eql([368, 54]);
// expect(rectangle.getSize()._round().toArray()).to.be.eql([368, 54]);
expect(rectangle.getSize()._round().toArray()).to.be.eql([148, 54]);
});

it('paint at [-180, -85]', function () {
Expand Down
117 changes: 65 additions & 52 deletions test/geometry/ExtentSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,85 +51,85 @@ describe('ExtentSpec', function () {
var subed2 = extent.sub([1, 1]);
var subed3 = extent.sub(new maptalks.Point(1, 1));
var subed4 = extent.substract([1, 1]);
var subed5 = extent.sub({ xmin : 1, ymin : 2, xmax : 3, ymax : 4 });
var subed5 = extent.sub({ xmin: 1, ymin: 2, xmax: 3, ymax: 4 });
expect(subed2.toJSON()).to.eql({
'xmin':0,
'ymin':1,
'xmax':2,
'ymax':3
'xmin': 0,
'ymin': 1,
'xmax': 2,
'ymax': 3
});
expect(subed3.toJSON()).to.eql({
'xmin':0,
'ymin':1,
'xmax':2,
'ymax':3
'xmin': 0,
'ymin': 1,
'xmax': 2,
'ymax': 3
});
expect(subed4.toJSON()).to.eql({
'xmin':0,
'ymin':1,
'xmax':2,
'ymax':3
'xmin': 0,
'ymin': 1,
'xmax': 2,
'ymax': 3
});
expect(subed5.toJSON()).to.eql({
'xmin':0,
'ymin':0,
'xmax':0,
'ymax':0
'xmin': 0,
'ymin': 0,
'xmax': 0,
'ymax': 0
});
});

it('add', function () {
var extent = new maptalks.Extent(1, 2, 3, 4);
var subed2 = extent.add([1, 1]);
var subed3 = extent.add(new maptalks.Point(1, 1));
var subed4 = extent.add({ xmin : 1, ymin : 2, xmax : 3, ymax : 4 });
var subed4 = extent.add({ xmin: 1, ymin: 2, xmax: 3, ymax: 4 });
expect(subed2.toJSON()).to.eql({
'xmin':2,
'ymin':3,
'xmax':4,
'ymax':5
'xmin': 2,
'ymin': 3,
'xmax': 4,
'ymax': 5
});
expect(subed3.toJSON()).to.eql({
'xmin':2,
'ymin':3,
'xmax':4,
'ymax':5
'xmin': 2,
'ymin': 3,
'xmax': 4,
'ymax': 5
});
expect(subed4.toJSON()).to.eql({
'xmin':2,
'ymin':4,
'xmax':6,
'ymax':8
'xmin': 2,
'ymin': 4,
'xmax': 6,
'ymax': 8
});
});

it('round', function () {
var extent = new maptalks.Extent(1.1, 2.5, 3.3, 4.2);
var rounded = extent.round();
expect(rounded.toJSON()).to.eql({
'xmin':1,
'ymin':3,
'xmax':3,
'ymax':4
'xmin': 1,
'ymin': 3,
'xmax': 3,
'ymax': 4
});
expect(extent.xmin).to.be.eql(1.1);
extent._round();
expect(extent.toJSON()).to.eql({
'xmin':1,
'ymin':3,
'xmax':3,
'ymax':4
'xmin': 1,
'ymin': 3,
'xmax': 3,
'ymax': 4
});
});

it('copy', function () {
var extent = new maptalks.Extent(1.1, 2.5, 3.3, 4.2);
var copied = extent.copy();
expect(copied.toJSON()).to.eql({
'xmin':1.1,
'ymin':2.5,
'xmax':3.3,
'ymax':4.2
'xmin': 1.1,
'ymin': 2.5,
'xmax': 3.3,
'ymax': 4.2
});
});

Expand Down Expand Up @@ -168,10 +168,10 @@ describe('ExtentSpec', function () {
var ext1 = new maptalks.Extent(1, 2, 3, 4);
var json = ext1.toJSON();
expect(json).to.eql({
'xmin':1,
'ymin':2,
'xmax':3,
'ymax':4
'xmin': 1,
'ymin': 2,
'xmax': 3,
'ymax': 4
});
});

Expand Down Expand Up @@ -201,7 +201,7 @@ describe('ExtentSpec', function () {
it('contains with projection', function () {
var ext = new maptalks.Extent([-170, -80, 170, 80], maptalks.projection.EPSG3857);

expect(ext.contains(new maptalks.Coordinate([-0.113049,51.49856]))).to.be.ok();
expect(ext.contains(new maptalks.Coordinate([-0.113049, 51.49856]))).to.be.ok();
});
});

Expand Down Expand Up @@ -265,7 +265,7 @@ describe('ExtentSpec', function () {
it('should contain', function () {
var container = document.createElement('div');
var map = new maptalks.Map(container, {
center: [104,31],
center: [104, 31],
zoom: 3
});
var center = map.getCenter(),
Expand All @@ -277,21 +277,34 @@ describe('ExtentSpec', function () {
it('should intersect', function () {
var proj = maptalks.projection.EPSG3857;
var ext1 = new maptalks.Extent(170, 80, -170, -80, proj);
ext1.antiMeridian = true;

//相当于 [170,80,190,-80],注意没有设置投影,所以 antiMeridian不生效
var ext2 = new maptalks.Extent(-180, 85, -180, 85);
expect(ext1.intersects(ext2)).to.be.ok();
expect(ext1.intersects(new maptalks.Extent(150, 10, -160, 20))).to.not.be.ok();
ext2.antiMeridian = true;
expect(ext1.intersects(ext2)).not.to.be.ok();

const ext3 = new maptalks.Extent(150, 10, -160, 20);
ext3.antiMeridian = true;
expect(ext1.intersects(ext3)).not.to.be.ok();

});

it('should combine', function () {
var proj = maptalks.projection.EPSG3857;
var ext1 = new maptalks.Extent(-170, -50, 170, 80, proj);
var ext2 = new maptalks.Extent(175, -40, -160, 80, proj);
ext1.antiMeridian = true;
ext2.antiMeridian = true;
var combined = ext1.combine(ext2);

expect(combined.xmin).to.eql(175);
// expect(combined.xmin).to.eql(175);
// expect(combined.ymin).to.approx(-50);
// expect(combined.xmax).to.eql(170);

expect(combined.xmin).to.eql(-190);
expect(combined.ymin).to.approx(-50);
expect(combined.xmax).to.eql(170);
expect(combined.xmax).to.eql(200);
//FIXME
// expect(combined.ymax).to.eql(-10);
});
Expand Down
Loading

0 comments on commit 53c72cc

Please sign in to comment.