diff --git a/packages/maptalks/src/core/util/path.ts b/packages/maptalks/src/core/util/path.ts index d90ca1b857..4a78bc0cd1 100644 --- a/packages/maptalks/src/core/util/path.ts +++ b/packages/maptalks/src/core/util/path.ts @@ -301,3 +301,29 @@ export function getMinMaxAltitude(altitude: number | number[] | number[][]): [nu return [min, max]; } +export function pointsToCoordinates(map, points: Point[], glRes: number, altitude: number): Coordinate[] { + const ring = []; + for (let i = 0, len = points.length; i < len; i++) { + const pt = points[i]; + const c = map.pointAtResToCoordinate(pt, glRes); + c.z = altitude; + ring[i] = c; + } + // ring.push(ring[0].copy()); + return ring; +} + +export function getEllipseGLSize(center: Coordinate, measurer, map, halfWidth: number, halfHeight: number) { + const glRes = map.getGLRes(); + const c1 = measurer.locate(center, halfWidth, 0); + const c2 = measurer.locate(center, 0, halfHeight); + const pt = map.coordToPointAtRes(center, glRes); + const p1 = map.coordToPointAtRes(c1, glRes); + const p2 = map.coordToPointAtRes(c2, glRes); + return { + glWidth: pt.distanceTo(p1), + glHeight: pt.distanceTo(p2), + glCenter: pt + } +} + diff --git a/packages/maptalks/src/geometry/Circle.ts b/packages/maptalks/src/geometry/Circle.ts index 1dc46dceb2..418fcacc00 100644 --- a/packages/maptalks/src/geometry/Circle.ts +++ b/packages/maptalks/src/geometry/Circle.ts @@ -1,5 +1,5 @@ import { extend, isNil } from '../core/util'; -import { withInEllipse } from '../core/util/path'; +import { getEllipseGLSize, pointsToCoordinates, withInEllipse } from '../core/util/path'; import Coordinate from '../geo/Coordinate'; import Extent from '../geo/Extent'; import Point from '../geo/Point'; @@ -14,7 +14,8 @@ import Polygon, { PolygonOptionsType, RingCoordinates, RingsCoordinates } from ' * @instance */ const options: CircleOptionsType = { - 'numberOfShellPoints': 60 + 'numberOfShellPoints': 60, + 'ignoreProjection': false }; /** @@ -93,6 +94,26 @@ export class Circle extends CenterMixin(Polygon) { radius = this.getRadius(); const shell = []; let rad, dx, dy; + const options = this.options as CircleOptionsType; + const ignoreProjection = options.ignoreProjection; + const map = this.getMap(); + + if (ignoreProjection && map) { + const glRes = map.getGLRes(); + const { glWidth, glHeight, glCenter } = getEllipseGLSize(center, measurer, map, radius, radius); + const r = Math.max(glWidth, glHeight); + const pts: Point[] = []; + for (let i = 0, len = numberOfPoints - 1; i < len; i++) { + rad = (360 * i / len) * Math.PI / 180; + const x = Math.cos(rad) * r + glCenter.x; + const y = Math.sin(rad) * r + glCenter.y; + const p = new Point(x, y); + pts[i] = p; + } + const ring = pointsToCoordinates(map, pts, glRes, center.z); + ring.push(ring[0].copy()); + return ring; + } for (let i = 0, len = numberOfPoints - 1; i < len; i++) { rad = (360 * i / len) * Math.PI / 180; dx = radius * Math.cos(rad); @@ -101,6 +122,7 @@ export class Circle extends CenterMixin(Polygon) { vertex.z = center.z; shell.push(vertex); } + shell.push(shell[0]); return shell; } @@ -221,4 +243,5 @@ export default Circle; export type CircleOptionsType = PolygonOptionsType & { numberOfShellPoints?: number; + ignoreProjection?: boolean; } diff --git a/packages/maptalks/src/geometry/Ellipse.ts b/packages/maptalks/src/geometry/Ellipse.ts index 9274ddca7e..47c1f90cf7 100644 --- a/packages/maptalks/src/geometry/Ellipse.ts +++ b/packages/maptalks/src/geometry/Ellipse.ts @@ -1,5 +1,5 @@ import { extend, isNil, pushIn } from '../core/util'; -import { withInEllipse } from '../core/util/path'; +import { getEllipseGLSize, pointsToCoordinates, withInEllipse } from '../core/util/path'; import Coordinate from '../geo/Coordinate'; import CenterMixin from './CenterMixin'; import Polygon, { PolygonOptionsType, RingCoordinates, RingsCoordinates } from './Polygon'; @@ -44,7 +44,8 @@ function angleT(numberOfShellPoints: number) { * @instance */ const options: EllipseOptionsType = { - 'numberOfShellPoints': 81 + 'numberOfShellPoints': 81, + 'ignoreProjection': false }; /** @@ -182,6 +183,41 @@ export class Ellipse extends CenterMixin(Polygon) { } let deg, rad, dx, dy; + const options = this.options as EllipseOptionsType; + const ignoreProjection = options.ignoreProjection; + const map = this.getMap(); + if (ignoreProjection && map) { + const glRes = map.getGLRes(); + const { glWidth, glHeight, glCenter } = getEllipseGLSize(center, measurer, map, width / 2, height / 2); + //gl width + const w = glWidth * 2; + //gl width + const h = glHeight * 2; + const s = Math.pow(w / 2, 2) * Math.pow(h / 2, 2), + sx = Math.pow(w / 2, 2), + sy = Math.pow(h / 2, 2); + const pts: Point[] = []; + for (let i = 0; i < angles.length; i++) { + deg = angles[i]; + rad = deg * Math.PI / 180; + dx = Math.sqrt(s / (sx * Math.pow(Math.tan(rad), 2) + sy)); + dy = Math.sqrt(s / (sy * Math.pow(1 / Math.tan(rad), 2) + sx)); + if (deg > 90 && deg < 270) { + dx *= -1; + } + if (deg > 180 && deg < 360) { + dy *= -1; + } + const p = glCenter.copy(); + p.x += dx; + p.y += dy; + pts[i] = p; + } + const ring = pointsToCoordinates(map, pts, glRes, center.z); + ring.push(ring[0].copy()); + return ring; + } + for (let i = 0; i < angles.length; i++) { deg = angles[i]; rad = deg * Math.PI / 180; @@ -319,4 +355,5 @@ export default Ellipse; export type EllipseOptionsType = PolygonOptionsType & { numberOfShellPoints?: number; debug?: boolean; + ignoreProjection?: boolean; } diff --git a/packages/maptalks/src/geometry/Sector.ts b/packages/maptalks/src/geometry/Sector.ts index 16a7f754c1..231b479d58 100644 --- a/packages/maptalks/src/geometry/Sector.ts +++ b/packages/maptalks/src/geometry/Sector.ts @@ -1,4 +1,5 @@ import { extend, isNil } from '../core/util'; +import { getEllipseGLSize, pointsToCoordinates } from '../core/util/path'; import Coordinate from '../geo/Coordinate'; import Extent from '../geo/Extent'; import Point from '../geo/Point'; @@ -135,6 +136,27 @@ export class Sector extends Circle { // startAngle = this.getStartAngle(), angle = endAngle - startAngle; let rad, dx, dy; + const options = this.options as SectorOptionsType; + const ignoreProjection = options.ignoreProjection; + const map = this.getMap(); + if (ignoreProjection && map) { + const glRes = map.getGLRes(); + const { glWidth, glHeight, glCenter } = getEllipseGLSize(center, measurer, map, radius, radius); + const r = Math.max(glWidth, glHeight); + const pts: Point[] = []; + for (let i = 0; i < numberOfPoints; i++) { + rad = (angle * i / (numberOfPoints - 1) + startAngle) * Math.PI / 180; + dx = radius * Math.cos(rad); + dy = radius * Math.sin(rad); + const x = Math.cos(rad) * r + glCenter.x; + const y = Math.sin(rad) * r + glCenter.y; + const p = new Point(x, y); + pts[i] = p; + } + const ring = pointsToCoordinates(map, pts, glRes, center.z); + ring.push(center.copy()); + return ring; + } for (let i = 0; i < numberOfPoints; i++) { rad = (angle * i / (numberOfPoints - 1) + startAngle) * Math.PI / 180; dx = radius * Math.cos(rad); diff --git a/packages/maptalks/src/renderer/geometry/VectorRenderer.ts b/packages/maptalks/src/renderer/geometry/VectorRenderer.ts index f5e88b626f..0c97dd7be1 100644 --- a/packages/maptalks/src/renderer/geometry/VectorRenderer.ts +++ b/packages/maptalks/src/renderer/geometry/VectorRenderer.ts @@ -110,6 +110,9 @@ const el = { } const map = this.getMap(); const altitude = this._getAltitude(); + if (this instanceof Ellipse || this instanceof Circle) { + return true; + } // when map is tilting, draw the circle/ellipse as a polygon by vertexes. return altitude > 0 || map.getPitch() || ((this instanceof Ellipse) && map.getBearing()); },