geojs 1.8.10 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/geo.js +3057 -176
- package/geo.lean.js +3056 -175
- package/geo.lean.min.js +10 -4
- package/geo.min.js +11 -5
- package/package.json +6 -5
- package/src/annotation/annotation.js +19 -2
- package/src/annotation/circleAnnotation.js +8 -1
- package/src/annotation/ellipseAnnotation.js +51 -1
- package/src/annotation/lineAnnotation.js +29 -20
- package/src/annotation/pointAnnotation.js +28 -23
- package/src/annotation/polygonAnnotation.js +52 -22
- package/src/annotation/rectangleAnnotation.js +42 -23
- package/src/annotation/squareAnnotation.js +8 -1
- package/src/annotationLayer.js +188 -2
- package/src/css/cursor-crosshair-difference.svg +4 -0
- package/src/css/cursor-crosshair-intersect.svg +4 -0
- package/src/css/cursor-crosshair-union.svg +4 -0
- package/src/css/cursor-crosshair-xor.svg +4 -0
- package/src/event.js +14 -0
- package/src/lineFeature.js +1 -1
- package/src/main.styl +8 -0
- package/src/polygonFeature.js +50 -0
- package/src/typedef.js +11 -0
- package/src/util/index.js +2 -1
- package/src/util/polyops.js +449 -0
package/src/annotationLayer.js
CHANGED
|
@@ -181,6 +181,44 @@ var annotationLayer = function (arg) {
|
|
|
181
181
|
m_this._updateFromEvent(update);
|
|
182
182
|
};
|
|
183
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Check if there is a current boolean operation.
|
|
186
|
+
*
|
|
187
|
+
* @returns {string?} Either undefined for no current boolean operation or
|
|
188
|
+
* the name of the operation.
|
|
189
|
+
*/
|
|
190
|
+
this.currentBooleanOperation = function () {
|
|
191
|
+
let op;
|
|
192
|
+
if (m_this._currentBooleanClass) {
|
|
193
|
+
op = m_this._currentBooleanClass.split('-')[1];
|
|
194
|
+
}
|
|
195
|
+
return op;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Check if the map is currently in a mode where we are adding an annotation
|
|
200
|
+
* with a boolean operation. If so, remove the current annotation from the
|
|
201
|
+
* layer, then apply it via the boolean operation.
|
|
202
|
+
*/
|
|
203
|
+
this._handleBooleanOperation = function () {
|
|
204
|
+
const op = m_this.currentBooleanOperation();
|
|
205
|
+
if (!op || !m_this.currentAnnotation || !m_this.currentAnnotation.toPolygonList) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const newAnnot = m_this.currentAnnotation;
|
|
209
|
+
m_this.removeAnnotation(m_this.currentAnnotation, false);
|
|
210
|
+
if (m_this.annotations().length || (op !== 'difference' && op !== 'intersect')) {
|
|
211
|
+
const evt = {
|
|
212
|
+
annotation: newAnnot,
|
|
213
|
+
operation: op
|
|
214
|
+
};
|
|
215
|
+
m_this.geoTrigger(geo_event.annotation.boolean, evt);
|
|
216
|
+
if (evt.cancel !== false) {
|
|
217
|
+
util.polyops[op](m_this, newAnnot.toPolygonList(), {correspond: {}, keepAnnotations: 'exact', style: m_this});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
184
222
|
/**
|
|
185
223
|
* Handle updating the current annotation based on an update state.
|
|
186
224
|
*
|
|
@@ -196,6 +234,7 @@ var annotationLayer = function (arg) {
|
|
|
196
234
|
m_this.mode(null);
|
|
197
235
|
break;
|
|
198
236
|
case 'done':
|
|
237
|
+
m_this._handleBooleanOperation();
|
|
199
238
|
m_this.mode(null);
|
|
200
239
|
break;
|
|
201
240
|
}
|
|
@@ -205,6 +244,27 @@ var annotationLayer = function (arg) {
|
|
|
205
244
|
}
|
|
206
245
|
};
|
|
207
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Check the state of the modifier keys and apply them if appropriate.
|
|
249
|
+
*
|
|
250
|
+
* @param {geo.event} evt The mouse move or click event.
|
|
251
|
+
*/
|
|
252
|
+
this._handleMouseMoveModifiers = function (evt) {
|
|
253
|
+
if (m_this.mode() !== m_this.modes.edit && m_this.currentAnnotation.options('allowBooleanOperations') && m_this.currentAnnotation._coordinates().length < 2) {
|
|
254
|
+
if (evt.modifiers) {
|
|
255
|
+
const mod = (evt.modifiers.shift ? 's' : '') + (evt.modifiers.ctrl ? 'c' : '') + (evt.modifiers.meta || evt.modifiers.alt ? 'a' : '');
|
|
256
|
+
if (m_this._currentBooleanClass === m_this._booleanClasses[mod]) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
m_this._currentBooleanClass = m_this._booleanClasses[mod];
|
|
260
|
+
const mapNode = m_this.map().node();
|
|
261
|
+
Object.values(m_this._booleanClasses).forEach((c) => {
|
|
262
|
+
mapNode.toggleClass(c, m_this._booleanClasses[mod] === c);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
208
268
|
/**
|
|
209
269
|
* Handle mouse movement. If there is a current annotation, the movement
|
|
210
270
|
* event is sent to it.
|
|
@@ -213,6 +273,7 @@ var annotationLayer = function (arg) {
|
|
|
213
273
|
*/
|
|
214
274
|
this._handleMouseMove = function (evt) {
|
|
215
275
|
if (m_this.mode() && m_this.currentAnnotation) {
|
|
276
|
+
m_this._handleMouseMoveModifiers(evt);
|
|
216
277
|
var update = m_this.currentAnnotation.mouseMove(evt);
|
|
217
278
|
if (update) {
|
|
218
279
|
m_this.modified();
|
|
@@ -316,6 +377,7 @@ var annotationLayer = function (arg) {
|
|
|
316
377
|
});
|
|
317
378
|
retrigger = true;
|
|
318
379
|
} else if (m_this.mode() && m_this.currentAnnotation) {
|
|
380
|
+
m_this._handleMouseMoveModifiers(evt);
|
|
319
381
|
update = m_this.currentAnnotation.mouseClick(evt);
|
|
320
382
|
m_this._updateFromEvent(update);
|
|
321
383
|
retrigger = !m_this.mode();
|
|
@@ -518,6 +580,15 @@ var annotationLayer = function (arg) {
|
|
|
518
580
|
edit: 'edit'
|
|
519
581
|
};
|
|
520
582
|
|
|
583
|
+
/* Keys are short-hand for preferred event modifiers. Values are classes to
|
|
584
|
+
* apply to the map node. */
|
|
585
|
+
this._booleanClasses = {
|
|
586
|
+
s: 'annotation-union',
|
|
587
|
+
sc: 'annotation-intersect',
|
|
588
|
+
c: 'annotation-difference',
|
|
589
|
+
sa: 'annotation-xor'
|
|
590
|
+
};
|
|
591
|
+
|
|
521
592
|
/**
|
|
522
593
|
* Get or set the current mode.
|
|
523
594
|
*
|
|
@@ -537,11 +608,15 @@ var annotationLayer = function (arg) {
|
|
|
537
608
|
if (arg === undefined) {
|
|
538
609
|
return m_mode;
|
|
539
610
|
}
|
|
540
|
-
if (arg !== m_mode || (arg === m_this.modes.edit && editAnnotation !== m_this.editAnnotation)) {
|
|
611
|
+
if (arg !== m_mode || (arg === m_this.modes.edit && editAnnotation !== m_this.editAnnotation) || (arg !== m_this.modes.edit && arg && !m_this.currentAnnotation)) {
|
|
541
612
|
var createAnnotation, actions,
|
|
542
613
|
mapNode = m_this.map().node(), oldMode = m_mode;
|
|
543
614
|
m_mode = arg;
|
|
544
615
|
mapNode.toggleClass('annotation-input', !!(m_mode && m_mode !== m_this.modes.edit));
|
|
616
|
+
if (!m_mode || m_mode === m_this.modes.edit) {
|
|
617
|
+
Object.values(m_this._booleanClasses).forEach((c) => mapNode.toggleClass(c, false));
|
|
618
|
+
m_this._currentBooleanClass = undefined;
|
|
619
|
+
}
|
|
545
620
|
if (!m_keyHandler) {
|
|
546
621
|
m_keyHandler = Mousetrap(mapNode[0]);
|
|
547
622
|
}
|
|
@@ -618,7 +693,12 @@ var annotationLayer = function (arg) {
|
|
|
618
693
|
*/
|
|
619
694
|
this.geojson = function (geojson, clear, gcs, includeCrs) {
|
|
620
695
|
if (geojson !== undefined) {
|
|
621
|
-
var reader = registry.createFileReader('geojsonReader', {
|
|
696
|
+
var reader = registry.createFileReader('geojsonReader', {
|
|
697
|
+
layer: m_this,
|
|
698
|
+
lineStyle: require('./annotation/lineAnnotation').defaults.style,
|
|
699
|
+
pointStyle: require('./annotation/pointAnnotation').defaults.style,
|
|
700
|
+
polygonStyle: require('./annotation/polygonAnnotation').defaults.style
|
|
701
|
+
});
|
|
622
702
|
if (!reader.canRead(geojson)) {
|
|
623
703
|
return;
|
|
624
704
|
}
|
|
@@ -1156,6 +1236,112 @@ var annotationLayer = function (arg) {
|
|
|
1156
1236
|
return m_this;
|
|
1157
1237
|
};
|
|
1158
1238
|
|
|
1239
|
+
/**
|
|
1240
|
+
* Return any annotation that has area as a polygon list: an array of
|
|
1241
|
+
* polygons, each of which is an array of polylines, each of which is an
|
|
1242
|
+
* array of points, each of which is a 2-tuple of numbers.
|
|
1243
|
+
*
|
|
1244
|
+
* @param {geo.util.polyop.spec} [opts] Ignored.
|
|
1245
|
+
* @returns {geo.polygonList} A list of polygons.
|
|
1246
|
+
*/
|
|
1247
|
+
this.toPolygonList = function (opts) {
|
|
1248
|
+
const poly = [];
|
|
1249
|
+
opts = opts || {};
|
|
1250
|
+
opts.annotationIndices = [];
|
|
1251
|
+
m_annotations.forEach((annotation, idx) => {
|
|
1252
|
+
if (annotation.toPolygonList) {
|
|
1253
|
+
annotation.toPolygonList(opts).forEach((p) => poly.push(p));
|
|
1254
|
+
opts.annotationIndices.push(idx);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
return poly;
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Replace appropriate annotations with a list of polygons.
|
|
1262
|
+
*
|
|
1263
|
+
* @param {geo.polygonList} poly A list of polygons.
|
|
1264
|
+
* @param {geo.util.polyop.spec} [opts] This contains annotationIndices and
|
|
1265
|
+
* correspondence used to track annotations.
|
|
1266
|
+
* @returns {this}
|
|
1267
|
+
*/
|
|
1268
|
+
this.fromPolygonList = function (poly, opts) {
|
|
1269
|
+
if (!poly || !poly.length) {
|
|
1270
|
+
return m_this;
|
|
1271
|
+
}
|
|
1272
|
+
/* One of 'all', 'exact', 'none'; default is 'exact' */
|
|
1273
|
+
const keep = opts.keepAnnotations;
|
|
1274
|
+
const keepIds = {};
|
|
1275
|
+
const reusedIds = {};
|
|
1276
|
+
let correspond, exact, annot;
|
|
1277
|
+
if (opts.annotationIndices && opts.correspond && opts.correspond.poly1 && opts.annotationIndices.length === opts.correspond.poly1.length) {
|
|
1278
|
+
correspond = opts.correspond.poly1;
|
|
1279
|
+
exact = opts.correspond.exact1;
|
|
1280
|
+
annot = m_this.annotations();
|
|
1281
|
+
}
|
|
1282
|
+
if (keep !== 'all' && keep !== 'none') {
|
|
1283
|
+
annot.forEach((oldAnnot, idx) => {
|
|
1284
|
+
if (opts.annotationIndices.indexOf(idx) < 0) {
|
|
1285
|
+
keepIds[oldAnnot.id()] = true;
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
const polyAnnot = [];
|
|
1290
|
+
poly.forEach((p, idx) => {
|
|
1291
|
+
p = p.map((h) => h.map((pt) => ({x: pt[0], y: pt[1]})));
|
|
1292
|
+
const result = {
|
|
1293
|
+
vertices: p.length === 1 ? p[0] : {outer: p[0], inner: p.slice(1)}
|
|
1294
|
+
};
|
|
1295
|
+
if (correspond) {
|
|
1296
|
+
for (let i = 0; i < correspond.length; i += 1) {
|
|
1297
|
+
if (correspond[i] && correspond[i].indexOf(idx) >= 0) {
|
|
1298
|
+
const orig = annot[opts.annotationIndices[i]];
|
|
1299
|
+
if (keep !== 'all' && keep !== 'none' && exact && exact[i] && exact[i].indexOf(idx) >= 0) {
|
|
1300
|
+
keepIds[orig.id()] = true;
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
if (keep !== 'all' && reusedIds[orig.id()] === undefined) {
|
|
1304
|
+
result.annotationId = orig.id();
|
|
1305
|
+
reusedIds[orig.id()] = true;
|
|
1306
|
+
}
|
|
1307
|
+
['name', 'description', 'label'].forEach((k) => {
|
|
1308
|
+
if (orig[k](undefined, true)) {
|
|
1309
|
+
result[k] = orig[k](undefined, true);
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
Object.entries(orig.options()).forEach(([key, value]) => {
|
|
1313
|
+
if (['showLabel', 'style'].indexOf(key) >= 0 || key.endsWith('Style')) {
|
|
1314
|
+
result[key] = value;
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
break;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
polyAnnot.push(result);
|
|
1322
|
+
});
|
|
1323
|
+
let update = false;
|
|
1324
|
+
// delete extant annotations
|
|
1325
|
+
if (keep !== 'all' && annot) {
|
|
1326
|
+
annot
|
|
1327
|
+
.filter((oldAnnot) => keepIds[oldAnnot.id()] === undefined)
|
|
1328
|
+
.forEach((oldAnnot) => {
|
|
1329
|
+
m_this.removeAnnotation(oldAnnot, false);
|
|
1330
|
+
update = true;
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
// add new annotations
|
|
1334
|
+
polyAnnot.forEach((p) => {
|
|
1335
|
+
m_this.addAnnotation(registry.createAnnotation('polygon', p), m_this.gcs(), false);
|
|
1336
|
+
update = true;
|
|
1337
|
+
});
|
|
1338
|
+
if (update) {
|
|
1339
|
+
m_this.modified();
|
|
1340
|
+
m_this.draw();
|
|
1341
|
+
}
|
|
1342
|
+
return m_this;
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1159
1345
|
/**
|
|
1160
1346
|
* Initialize.
|
|
1161
1347
|
*
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
|
|
2
|
+
<path d="M0,11l11,0l0,-11l2,0l0,11l11,0l0,2l-11,0l0,11l-2,0l0,-11l-11,0z" fill="black" stroke-width="0"/>
|
|
3
|
+
<path d="M24,32l0,-4a4,4,0,0,1,8,0l0,4l-2,0l0,-4a2,2,0,0,0,-4,0l0,4z" fill="black" stroke-width="0"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
|
|
2
|
+
<path d="M0,11l11,0l0,-11l2,0l0,11l11,0l0,2l-11,0l0,11l-2,0l0,-11l-11,0z" fill="black" stroke-width="0"/>
|
|
3
|
+
<path d="M24,27l3,0l0,-3l2,0l0,3l3,0l0,2l-3,0l0,3l-2,0l0,-3l-3,0z" fill="black" stroke-width="0"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32">
|
|
2
|
+
<path d="M0,11l11,0l0,-11l2,0l0,11l11,0l0,2l-11,0l0,11l-2,0l0,-11l-11,0z" fill="black" stroke-width="0"/>
|
|
3
|
+
<path d="M32,30.59l-1.41,1.41l-2.12,-2.12l-2.12,2.12l-1.41,-1.41l2.12,-2.12l-2.12,-2.12l1.41,-1.41l2.12,2.12l2.12,-2.12l1.41,1.41l-2.12,2.12z" fill="black" stroke-width="0"/>
|
|
4
|
+
</svg>
|
package/src/event.js
CHANGED
|
@@ -692,4 +692,18 @@ geo_event.annotation.state = 'geo_annotation_state';
|
|
|
692
692
|
*/
|
|
693
693
|
geo_event.annotation.mode = 'geo_annotation_mode';
|
|
694
694
|
|
|
695
|
+
/**
|
|
696
|
+
* Triggered when an annotation can be combined via a boolean operation (union,
|
|
697
|
+
* intersect, difference, xor).
|
|
698
|
+
*
|
|
699
|
+
* @event geo.event.annotation.boolean
|
|
700
|
+
* @type {geo.event.base}
|
|
701
|
+
* @property {geo.annotation} annotation The annotation that is being operated
|
|
702
|
+
* on.
|
|
703
|
+
* @property {string} operation The operation being performed.
|
|
704
|
+
* @property {boolean} [cancel] If the handle sets this to false, don't apply
|
|
705
|
+
* the operation to the annotation layer.
|
|
706
|
+
*/
|
|
707
|
+
geo_event.annotation.boolean = 'geo_annotation_boolean';
|
|
708
|
+
|
|
695
709
|
module.exports = geo_event;
|
package/src/lineFeature.js
CHANGED
|
@@ -182,7 +182,7 @@ var lineFeature = function (arg) {
|
|
|
182
182
|
const current = lineRecord[j];
|
|
183
183
|
let p = posFunc(current, j, d, index);
|
|
184
184
|
if (onlyInvertedY) {
|
|
185
|
-
p.y
|
|
185
|
+
p = {x: p.x, y: -p.y};
|
|
186
186
|
} else if (gcs !== mapgcs) {
|
|
187
187
|
p = transform.transformCoordinates(gcs, mapgcs, p);
|
|
188
188
|
}
|
package/src/main.styl
CHANGED
|
@@ -49,6 +49,14 @@
|
|
|
49
49
|
|
|
50
50
|
&.annotation-input
|
|
51
51
|
cursor crosshair
|
|
52
|
+
&.annotation-intersect
|
|
53
|
+
cursor embedurl("./css/cursor-crosshair-intersect.svg") 12 12,crosshair
|
|
54
|
+
&.annotation-difference
|
|
55
|
+
cursor embedurl("./css/cursor-crosshair-difference.svg") 12 12,crosshair
|
|
56
|
+
&.annotation-union
|
|
57
|
+
cursor embedurl("./css/cursor-crosshair-union.svg") 12 12,crosshair
|
|
58
|
+
&.annotation-xor
|
|
59
|
+
cursor embedurl("./css/cursor-crosshair-xor.svg") 12 12,crosshair
|
|
52
60
|
|
|
53
61
|
&.highlight-focus
|
|
54
62
|
&:after
|
package/src/polygonFeature.js
CHANGED
|
@@ -686,6 +686,56 @@ var polygonFeature = function (arg) {
|
|
|
686
686
|
});
|
|
687
687
|
};
|
|
688
688
|
|
|
689
|
+
/**
|
|
690
|
+
* Return the polygons as a polygon list: an array of polygons, each of which
|
|
691
|
+
* is an array of polylines, each of which is an array of points, each of
|
|
692
|
+
* which is a 2-tuple of numbers.
|
|
693
|
+
*
|
|
694
|
+
* @param {geo.util.polyop.spec} [opts] Ignored.
|
|
695
|
+
* @returns {geo.polygonList} A list of polygons.
|
|
696
|
+
*/
|
|
697
|
+
this.toPolygonList = function (opts) {
|
|
698
|
+
const polyFunc = m_this.style.get('polygon');
|
|
699
|
+
const posFunc = m_this.style.get('position');
|
|
700
|
+
return m_this.data().map((d, i) => {
|
|
701
|
+
const polygon = polyFunc(d, i);
|
|
702
|
+
const outer = polygon.outer || (Array.isArray(polygon) ? polygon : []);
|
|
703
|
+
if (outer.length < 3) {
|
|
704
|
+
return [];
|
|
705
|
+
}
|
|
706
|
+
const resp = [outer.map((p, j) => {
|
|
707
|
+
const pos = posFunc(p, j, d, i);
|
|
708
|
+
return [pos.x, pos.y];
|
|
709
|
+
})];
|
|
710
|
+
if (polygon.inner) {
|
|
711
|
+
polygon.inner.forEach((h) => {
|
|
712
|
+
resp.push(h.map((p, j) => {
|
|
713
|
+
const pos = posFunc(p, j, d, i);
|
|
714
|
+
return [pos.x, pos.y];
|
|
715
|
+
}));
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
return resp;
|
|
719
|
+
});
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Set the data, position accessor, and polygon accessor to use a list of
|
|
724
|
+
* polygons.
|
|
725
|
+
*
|
|
726
|
+
* @param {geo.polygonList} poly A list of polygons.
|
|
727
|
+
* @param {geo.util.polyop.spec} [opts] Ignored.
|
|
728
|
+
* @returns {this}
|
|
729
|
+
*/
|
|
730
|
+
this.fromPolygonList = function (poly, opts) {
|
|
731
|
+
m_this.style({
|
|
732
|
+
position: (p) => ({x: p[0], y: p[1]}),
|
|
733
|
+
polygon: (p) => ({outer: p[0], inner: p.slice(1)})
|
|
734
|
+
});
|
|
735
|
+
m_this.data(poly);
|
|
736
|
+
return m_this;
|
|
737
|
+
};
|
|
738
|
+
|
|
689
739
|
/**
|
|
690
740
|
* Destroy.
|
|
691
741
|
*/
|
package/src/typedef.js
CHANGED
|
@@ -54,6 +54,17 @@
|
|
|
54
54
|
* @property {number} [z=0] Altitude coordinate.
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* General represention of a point on the earth. The coordinates are most
|
|
59
|
+
* commonly in longitude and latitude, but the coordinate system is changed
|
|
60
|
+
* by the interface gcs. This is a list of numbers consisting of either [x, y]
|
|
61
|
+
* or [x, y, z], where therse are the horizontal, vertical, and altitude
|
|
62
|
+
* coordinates.
|
|
63
|
+
*
|
|
64
|
+
* @typedef geo.geoPositionFlat
|
|
65
|
+
* @type {number[]}
|
|
66
|
+
*/
|
|
67
|
+
|
|
57
68
|
/**
|
|
58
69
|
* General represention of a two-dimensional point in any coordinate system.
|
|
59
70
|
*
|