geojs 1.7.3 → 1.8.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/CHANGELOG.md +18 -0
- package/geo.js +731 -143
- package/geo.lean.js +730 -142
- package/geo.lean.min.js +3 -3
- package/geo.min.js +5 -5
- package/package.json +3 -3
- package/src/annotation.js +300 -33
- package/src/annotationLayer.js +18 -23
- package/src/geojsonReader.js +68 -1
- package/src/map.js +1 -2
- package/src/mapInteractor.js +21 -6
- package/src/markerFeature.js +21 -6
- package/src/pixelmapLayer.js +8 -2
- package/src/registry.js +5 -0
- package/src/trackFeature.js +1 -1
- package/src/ui/colorLegendWidget.js +1 -1
- package/src/ui/scaleWidget.js +1 -1
- package/src/webgl/markerFeature.js +3 -2
- package/src/webgl/tileLayer.js +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "geojs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "JavaScript Geo Visualization and Analysis Library",
|
|
5
5
|
"homepage": "https://github.com/OpenGeoscience/geojs",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"express": "^4.17.2",
|
|
53
53
|
"fs-extra": "^10.0.0",
|
|
54
54
|
"glob": "^7.2.0",
|
|
55
|
-
"glslang-validator-prebuilt": "0.0.
|
|
55
|
+
"glslang-validator-prebuilt": "^0.0.7",
|
|
56
56
|
"imports-loader": "^3.1.1",
|
|
57
57
|
"jaguarjs-jsdoc": "^1.0.2",
|
|
58
58
|
"jasmine-core": "^4.0.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"jsdoc": "^3.6.10",
|
|
61
61
|
"jsdoc-autoprivate": "0.0.1",
|
|
62
62
|
"jsonlint-mod": "^1.7.6",
|
|
63
|
-
"jstransformer-markdown-it": "
|
|
63
|
+
"jstransformer-markdown-it": "jstransformers/jstransformer-markdown-it#snyk-fix-9cae6682d6a0ba4f72fd9fec4e488acd",
|
|
64
64
|
"karma": "^6.3.15",
|
|
65
65
|
"karma-chrome-launcher": "^3.1.0",
|
|
66
66
|
"karma-coverage": "^2.1.1",
|
package/src/annotation.js
CHANGED
|
@@ -6,6 +6,7 @@ var transform = require('./transform');
|
|
|
6
6
|
var util = require('./util');
|
|
7
7
|
var registerAnnotation = require('./registry').registerAnnotation;
|
|
8
8
|
var lineFeature = require('./lineFeature');
|
|
9
|
+
var markerFeature = require('./markerFeature');
|
|
9
10
|
var pointFeature = require('./pointFeature');
|
|
10
11
|
var polygonFeature = require('./polygonFeature');
|
|
11
12
|
var textFeature = require('./textFeature');
|
|
@@ -127,6 +128,14 @@ var annotation = function (type, args) {
|
|
|
127
128
|
delete m_options.label;
|
|
128
129
|
delete m_options.description;
|
|
129
130
|
|
|
131
|
+
if (m_options.constraint) {
|
|
132
|
+
if (util.isFunction(m_options.constraint)) {
|
|
133
|
+
this._selectionConstraint = m_options.constraint;
|
|
134
|
+
} else {
|
|
135
|
+
this._selectionConstraint = constrainAspectRatio(m_options.constraint);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
/**
|
|
131
140
|
* Clean up any resources that the annotation is using.
|
|
132
141
|
*/
|
|
@@ -420,7 +429,7 @@ var annotation = function (type, args) {
|
|
|
420
429
|
};
|
|
421
430
|
|
|
422
431
|
/**
|
|
423
|
-
*
|
|
432
|
+
* Get or set options.
|
|
424
433
|
*
|
|
425
434
|
* @param {string|object} [arg1] If `undefined`, return the options object.
|
|
426
435
|
* If a string, either set or return the option of that name. If an
|
|
@@ -485,7 +494,7 @@ var annotation = function (type, args) {
|
|
|
485
494
|
};
|
|
486
495
|
|
|
487
496
|
/**
|
|
488
|
-
*
|
|
497
|
+
* Get or set style.
|
|
489
498
|
*
|
|
490
499
|
* @param {string|object} [arg1] If `undefined`, return the current style
|
|
491
500
|
* object. If a string and `arg2` is undefined, return the style
|
|
@@ -1182,10 +1191,10 @@ function continuousVerticesProcessAction(m_this, evt, name) {
|
|
|
1182
1191
|
* finished rectangle. This uses styles for {@link geo.polygonFeature}.
|
|
1183
1192
|
* @property {geo.polygonFeature.styleSpec} [editStyle] The style to apply to a
|
|
1184
1193
|
* rectangle in edit mode.
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1194
|
+
* @property {number|number[]|function} [constraint] If specified, an aspect
|
|
1195
|
+
* ratio or list of aspect ratios to constraint the rectangle to. If a
|
|
1196
|
+
* function, a selection constraint function to call to adjust the
|
|
1197
|
+
* rectangle.
|
|
1189
1198
|
*/
|
|
1190
1199
|
|
|
1191
1200
|
/**
|
|
@@ -1200,11 +1209,12 @@ function continuousVerticesProcessAction(m_this, evt, name) {
|
|
|
1200
1209
|
* @extends geo.annotation
|
|
1201
1210
|
*
|
|
1202
1211
|
* @param {geo.rectangleAnnotation.spec?} [args] Options for the annotation.
|
|
1212
|
+
* @param {string} [annotationName='rectangle'] Override the annotation name.
|
|
1203
1213
|
*/
|
|
1204
|
-
var rectangleAnnotation = function (args) {
|
|
1214
|
+
var rectangleAnnotation = function (args, annotationName) {
|
|
1205
1215
|
'use strict';
|
|
1206
1216
|
if (!(this instanceof rectangleAnnotation)) {
|
|
1207
|
-
return new rectangleAnnotation(args);
|
|
1217
|
+
return new rectangleAnnotation(args, annotationName);
|
|
1208
1218
|
}
|
|
1209
1219
|
|
|
1210
1220
|
args = $.extend(true, {}, {
|
|
@@ -1232,7 +1242,7 @@ var rectangleAnnotation = function (args) {
|
|
|
1232
1242
|
}, args || {});
|
|
1233
1243
|
args.corners = args.corners || args.coordinates || [];
|
|
1234
1244
|
delete args.coordinates;
|
|
1235
|
-
annotation.call(this, 'rectangle', args);
|
|
1245
|
+
annotation.call(this, annotationName || 'rectangle', args);
|
|
1236
1246
|
|
|
1237
1247
|
var m_this = this,
|
|
1238
1248
|
s_actions = this.actions,
|
|
@@ -1257,7 +1267,8 @@ var rectangleAnnotation = function (args) {
|
|
|
1257
1267
|
owner: annotationActionOwner,
|
|
1258
1268
|
input: 'left',
|
|
1259
1269
|
modifiers: {shift: false, ctrl: false},
|
|
1260
|
-
selectionRectangle: true
|
|
1270
|
+
selectionRectangle: true,
|
|
1271
|
+
selectionConstraint: this._selectionConstraint
|
|
1261
1272
|
}];
|
|
1262
1273
|
default:
|
|
1263
1274
|
return s_actions.apply(m_this, arguments);
|
|
@@ -1288,6 +1299,9 @@ var rectangleAnnotation = function (args) {
|
|
|
1288
1299
|
map.displayToGcs({x: evt.upperRight.x, y: evt.upperRight.y}, null),
|
|
1289
1300
|
map.displayToGcs({x: evt.upperRight.x, y: evt.lowerLeft.y}, null)
|
|
1290
1301
|
];
|
|
1302
|
+
if (this._selectionConstraint && evt.mouse && evt.state.origin) {
|
|
1303
|
+
this._selectionConstraint(evt.mouse.mapgcs, evt.state.origin.mapgcs, corners);
|
|
1304
|
+
}
|
|
1291
1305
|
/* Don't keep rectangles that have nearly zero area in display pixels */
|
|
1292
1306
|
if (layer.displayDistance(corners[0], null, corners[1], null) *
|
|
1293
1307
|
layer.displayDistance(corners[0], null, corners[3], null) < 0.01) {
|
|
@@ -1307,29 +1321,17 @@ var rectangleAnnotation = function (args) {
|
|
|
1307
1321
|
var opt = m_this.options(),
|
|
1308
1322
|
state = m_this.state(),
|
|
1309
1323
|
features;
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
polygon: opt.corners,
|
|
1317
|
-
style: m_this.styleForState(state)
|
|
1318
|
-
}
|
|
1319
|
-
}];
|
|
1324
|
+
features = [];
|
|
1325
|
+
if (opt.corners && opt.corners.length >= 4) {
|
|
1326
|
+
features = [{
|
|
1327
|
+
polygon: {
|
|
1328
|
+
polygon: opt.corners,
|
|
1329
|
+
style: m_this.styleForState(state)
|
|
1320
1330
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
polygon: opt.corners,
|
|
1326
|
-
style: m_this.styleForState(state)
|
|
1327
|
-
}
|
|
1328
|
-
}];
|
|
1329
|
-
if (state === annotationState.edit) {
|
|
1330
|
-
m_this._addEditHandles(features, opt.corners);
|
|
1331
|
-
}
|
|
1332
|
-
break;
|
|
1331
|
+
}];
|
|
1332
|
+
}
|
|
1333
|
+
if (state === annotationState.edit) {
|
|
1334
|
+
m_this._addEditHandles(features, opt.corners);
|
|
1333
1335
|
}
|
|
1334
1336
|
return features;
|
|
1335
1337
|
};
|
|
@@ -1410,6 +1412,9 @@ var rectangleAnnotation = function (args) {
|
|
|
1410
1412
|
corners[2] = $.extend({}, evt.mapgcs);
|
|
1411
1413
|
corners[1] = map.displayToGcs(c1, null);
|
|
1412
1414
|
corners[3] = map.displayToGcs(c3, null);
|
|
1415
|
+
if (this._selectionConstraint) {
|
|
1416
|
+
this._selectionConstraint(evt.mapgcs, corners[0], corners);
|
|
1417
|
+
}
|
|
1413
1418
|
};
|
|
1414
1419
|
|
|
1415
1420
|
/**
|
|
@@ -1530,6 +1535,9 @@ var rectangleAnnotation = function (args) {
|
|
|
1530
1535
|
corners[(index + 1) % 4].y += delta1.y;
|
|
1531
1536
|
corners[(index + 3) % 4].x += delta2.x;
|
|
1532
1537
|
corners[(index + 3) % 4].y += delta2.y;
|
|
1538
|
+
if (this._selectionConstraint) {
|
|
1539
|
+
corners = this._selectionConstraint(evt.mouse.mapgcs, evt.state.origin.mapgcs, corners, 'vertex', ang, index).corners;
|
|
1540
|
+
}
|
|
1533
1541
|
m_this.options('corners', corners);
|
|
1534
1542
|
return true;
|
|
1535
1543
|
case 'edge':
|
|
@@ -1545,6 +1553,9 @@ var rectangleAnnotation = function (args) {
|
|
|
1545
1553
|
corners[index].y += delta.y;
|
|
1546
1554
|
corners[(index + 1) % 4].x += delta.x;
|
|
1547
1555
|
corners[(index + 1) % 4].y += delta.y;
|
|
1556
|
+
if (this._selectionConstraint) {
|
|
1557
|
+
corners = this._selectionConstraint(evt.mouse.mapgcs, evt.state.origin.mapgcs, corners, 'edge', ang, index).corners;
|
|
1558
|
+
}
|
|
1548
1559
|
m_this.options('corners', corners);
|
|
1549
1560
|
return true;
|
|
1550
1561
|
}
|
|
@@ -1557,6 +1568,127 @@ var rectangleRequiredFeatures = {};
|
|
|
1557
1568
|
rectangleRequiredFeatures[polygonFeature.capabilities.feature] = true;
|
|
1558
1569
|
registerAnnotation('rectangle', rectangleAnnotation, rectangleRequiredFeatures);
|
|
1559
1570
|
|
|
1571
|
+
/**
|
|
1572
|
+
* Square annotation class.
|
|
1573
|
+
*
|
|
1574
|
+
* Squares are a subset of rectangles with a fixed aspect ratio.
|
|
1575
|
+
*
|
|
1576
|
+
* @class
|
|
1577
|
+
* @alias geo.squareAnnotation
|
|
1578
|
+
* @extends geo.annotation
|
|
1579
|
+
*
|
|
1580
|
+
* @param {geo.squareAnnotation.spec?} [args] Options for the annotation.
|
|
1581
|
+
* @param {string} [annotationName='square'] Override the annotation name.
|
|
1582
|
+
*/
|
|
1583
|
+
var squareAnnotation = function (args, annotationName) {
|
|
1584
|
+
'use strict';
|
|
1585
|
+
args = $.extend({}, args, {constraint: 1});
|
|
1586
|
+
if (!(this instanceof squareAnnotation)) {
|
|
1587
|
+
return new squareAnnotation(args, annotationName);
|
|
1588
|
+
}
|
|
1589
|
+
rectangleAnnotation.call(this, args, annotationName || 'square');
|
|
1590
|
+
};
|
|
1591
|
+
inherit(squareAnnotation, rectangleAnnotation);
|
|
1592
|
+
var squareRequiredFeatures = {};
|
|
1593
|
+
squareRequiredFeatures[polygonFeature.capabilities.feature] = true;
|
|
1594
|
+
registerAnnotation('square', squareAnnotation, squareRequiredFeatures);
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Ellipse annotation class.
|
|
1598
|
+
*
|
|
1599
|
+
* Ellipses are always rendered as markers.
|
|
1600
|
+
*
|
|
1601
|
+
* @class
|
|
1602
|
+
* @alias geo.ellipseAnnotation
|
|
1603
|
+
* @extends geo.annotation
|
|
1604
|
+
*
|
|
1605
|
+
* @param {geo.ellipseAnnotation.spec?} [args] Options for the annotation.
|
|
1606
|
+
* @param {string} [annotationName='ellipse'] Override the annotation name.
|
|
1607
|
+
*/
|
|
1608
|
+
var ellipseAnnotation = function (args, annotationName) {
|
|
1609
|
+
'use strict';
|
|
1610
|
+
if (!(this instanceof ellipseAnnotation)) {
|
|
1611
|
+
return new ellipseAnnotation(args, annotationName);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
rectangleAnnotation.call(this, args, annotationName || 'ellipse');
|
|
1615
|
+
|
|
1616
|
+
var m_this = this;
|
|
1617
|
+
|
|
1618
|
+
/**
|
|
1619
|
+
* Get a list of renderable features for this annotation.
|
|
1620
|
+
*
|
|
1621
|
+
* @returns {array} An array of features.
|
|
1622
|
+
*/
|
|
1623
|
+
this.features = function () {
|
|
1624
|
+
var opt = m_this.options(),
|
|
1625
|
+
state = m_this.state(),
|
|
1626
|
+
features;
|
|
1627
|
+
features = [];
|
|
1628
|
+
if (opt.corners && opt.corners.length >= 4) {
|
|
1629
|
+
const style = m_this.styleForState(state);
|
|
1630
|
+
const w = ((opt.corners[0].x - opt.corners[1].x) ** 2 + (opt.corners[0].y - opt.corners[1].y) ** 2) ** 0.5;
|
|
1631
|
+
const h = ((opt.corners[0].x - opt.corners[3].x) ** 2 + (opt.corners[0].y - opt.corners[3].y) ** 2) ** 0.5;
|
|
1632
|
+
const radius = Math.max(w, h) / 2 / m_this.layer().map().unitsPerPixel(0);
|
|
1633
|
+
const aspect = w ? h / w : 1e20;
|
|
1634
|
+
const rotation = -Math.atan2(opt.corners[1].y - opt.corners[0].y, opt.corners[1].x - opt.corners[0].x);
|
|
1635
|
+
features = [{
|
|
1636
|
+
marker: {
|
|
1637
|
+
x: (opt.corners[0].x + opt.corners[1].x + opt.corners[2].x + opt.corners[3].x) / 4,
|
|
1638
|
+
y: (opt.corners[0].y + opt.corners[1].y + opt.corners[2].y + opt.corners[3].y) / 4,
|
|
1639
|
+
style: $.extend(
|
|
1640
|
+
{}, style,
|
|
1641
|
+
{
|
|
1642
|
+
radius: radius,
|
|
1643
|
+
symbolValue: aspect,
|
|
1644
|
+
rotation: rotation,
|
|
1645
|
+
strokeOffset: 0,
|
|
1646
|
+
radiusIncludesStroke: false,
|
|
1647
|
+
scaleWithZoom: markerFeature.scaleMode.fill,
|
|
1648
|
+
rotateWithMap: true,
|
|
1649
|
+
strokeOpacity: style.stroke === false ? 0 : style.strokeOpacity,
|
|
1650
|
+
fillOpacity: style.fill === false ? 0 : style.fillOpacity
|
|
1651
|
+
})
|
|
1652
|
+
}
|
|
1653
|
+
}];
|
|
1654
|
+
}
|
|
1655
|
+
if (state === annotationState.edit) {
|
|
1656
|
+
m_this._addEditHandles(features, opt.corners);
|
|
1657
|
+
}
|
|
1658
|
+
return features;
|
|
1659
|
+
};
|
|
1660
|
+
};
|
|
1661
|
+
inherit(ellipseAnnotation, rectangleAnnotation);
|
|
1662
|
+
|
|
1663
|
+
var ellipseRequiredFeatures = {};
|
|
1664
|
+
ellipseRequiredFeatures[markerFeature.capabilities.feature] = true;
|
|
1665
|
+
registerAnnotation('ellipse', ellipseAnnotation, ellipseRequiredFeatures);
|
|
1666
|
+
|
|
1667
|
+
/**
|
|
1668
|
+
* Circle annotation class.
|
|
1669
|
+
*
|
|
1670
|
+
* Circles are a subset of rectangles with a fixed aspect ratio.
|
|
1671
|
+
*
|
|
1672
|
+
* @class
|
|
1673
|
+
* @alias geo.circleAnnotation
|
|
1674
|
+
* @extends geo.annotation
|
|
1675
|
+
*
|
|
1676
|
+
* @param {geo.circleAnnotation.spec?} [args] Options for the annotation.
|
|
1677
|
+
* @param {string} [annotationName='circle'] Override the annotation name.
|
|
1678
|
+
*/
|
|
1679
|
+
var circleAnnotation = function (args, annotationName) {
|
|
1680
|
+
'use strict';
|
|
1681
|
+
args = $.extend({}, args, {constraint: 1});
|
|
1682
|
+
if (!(this instanceof circleAnnotation)) {
|
|
1683
|
+
return new circleAnnotation(args, annotationName);
|
|
1684
|
+
}
|
|
1685
|
+
ellipseAnnotation.call(this, args, annotationName || 'circle');
|
|
1686
|
+
};
|
|
1687
|
+
inherit(circleAnnotation, ellipseAnnotation);
|
|
1688
|
+
var circleRequiredFeatures = {};
|
|
1689
|
+
circleRequiredFeatures[markerFeature.capabilities.feature] = true;
|
|
1690
|
+
registerAnnotation('circle', circleAnnotation, circleRequiredFeatures);
|
|
1691
|
+
|
|
1560
1692
|
/**
|
|
1561
1693
|
* Polygon annotation specification. Extends {@link geo.annotation.spec}.
|
|
1562
1694
|
*
|
|
@@ -2377,6 +2509,137 @@ var pointRequiredFeatures = {};
|
|
|
2377
2509
|
pointRequiredFeatures[pointFeature.capabilities.feature] = true;
|
|
2378
2510
|
registerAnnotation('point', pointAnnotation, pointRequiredFeatures);
|
|
2379
2511
|
|
|
2512
|
+
/**
|
|
2513
|
+
* Return a function that can be used as a selectionConstraint that requires
|
|
2514
|
+
* that the aspect ratio of a rectangle-like selection is a specific value or
|
|
2515
|
+
* range of values.
|
|
2516
|
+
*
|
|
2517
|
+
* @param {number|number[]} ratio Either a single aspect ratio or a list of
|
|
2518
|
+
* allowed aspect ratios. For instance, 1 will require that the selection
|
|
2519
|
+
* square, 2 would require that it is twice as wide as tall, [2, 1/2] would
|
|
2520
|
+
* allow it to be twice as wide or half as wide as it is tall.
|
|
2521
|
+
* @returns {function} A function that can be passed to the mapIterator
|
|
2522
|
+
* selectionConstraint or to an annotation constraint function.
|
|
2523
|
+
*/
|
|
2524
|
+
function constrainAspectRatio(ratio) {
|
|
2525
|
+
const ratios = Array.isArray(ratio) ? ratio : [ratio];
|
|
2526
|
+
|
|
2527
|
+
/**
|
|
2528
|
+
* Constrain a mouse action or annotation action to a list of aspect ratios.
|
|
2529
|
+
*
|
|
2530
|
+
* @param {geo.geoPosition} pos Mouse or new location in map gcs.
|
|
2531
|
+
* @param {geo.geoPosition} origin Origin in map gcs when the activity
|
|
2532
|
+
* started.
|
|
2533
|
+
* @param {geo.geoPosition} [corners] If specified, an array of corner
|
|
2534
|
+
* locations in mapgcs. This may be modified.
|
|
2535
|
+
* @param {string?} [mode] 'edge', 'vertex' or falsy. A falsy value implies
|
|
2536
|
+
* this is just the most recent point in the annotation, otherwise it is
|
|
2537
|
+
* the portion of the annotation being modified.
|
|
2538
|
+
* @param {number} [ang] A list of angles of each side of the polygon
|
|
2539
|
+
* represented by corners or the original annotation.
|
|
2540
|
+
* @param {integer} [index] The specific vertex or edge that is being
|
|
2541
|
+
* modified.
|
|
2542
|
+
* @returns {object} An object with the ``origin`` (this is what is passed
|
|
2543
|
+
* in), a new position as ``pos``, and the updated corners as ``corners``.
|
|
2544
|
+
*/
|
|
2545
|
+
function constraintFunction(pos, origin, corners, mode, ang, index) {
|
|
2546
|
+
let newpos = pos;
|
|
2547
|
+
let best;
|
|
2548
|
+
if (!corners) {
|
|
2549
|
+
corners = [
|
|
2550
|
+
{x: origin.x, y: origin.y},
|
|
2551
|
+
{x: pos.x, y: origin.y},
|
|
2552
|
+
{x: pos.x, y: pos.y},
|
|
2553
|
+
{x: origin.x, y: pos.y}
|
|
2554
|
+
];
|
|
2555
|
+
}
|
|
2556
|
+
if (mode) {
|
|
2557
|
+
/* Edit a vertex or edge */
|
|
2558
|
+
const i1 = (index + 1) % 4;
|
|
2559
|
+
const i2 = (index + 2) % 4;
|
|
2560
|
+
const i3 = (index + 3) % 4;
|
|
2561
|
+
const dist1 = ((corners[index].x - corners[i1].x) ** 2 + (corners[index].y - corners[i1].y) ** 2) ** 0.5;
|
|
2562
|
+
const dist3 = ((corners[index].x - corners[i3].x) ** 2 + (corners[index].y - corners[i3].y) ** 2) ** 0.5;
|
|
2563
|
+
const area = Math.abs(dist1 * dist3);
|
|
2564
|
+
let shape, edge;
|
|
2565
|
+
ratios.forEach((ratio) => {
|
|
2566
|
+
if (ratio !== 1 && !(index % 2)) {
|
|
2567
|
+
ratio = 1.0 / ratio;
|
|
2568
|
+
}
|
|
2569
|
+
const width = (area * ratio) ** 0.5;
|
|
2570
|
+
const score = (width - dist3) ** 2 + (width / ratio - dist1) ** 2;
|
|
2571
|
+
if (best === undefined || score < best) {
|
|
2572
|
+
best = score;
|
|
2573
|
+
shape = {
|
|
2574
|
+
w: width,
|
|
2575
|
+
h: width / ratio
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
const ang1 = ang[i1];
|
|
2580
|
+
const delta1 = {
|
|
2581
|
+
x: -shape.w * Math.cos(ang1),
|
|
2582
|
+
y: -shape.w * Math.sin(ang1)
|
|
2583
|
+
};
|
|
2584
|
+
const ang2 = ang[index];
|
|
2585
|
+
const delta2 = {
|
|
2586
|
+
x: -shape.h * Math.cos(ang2),
|
|
2587
|
+
y: -shape.h * Math.sin(ang2)
|
|
2588
|
+
};
|
|
2589
|
+
switch (mode) {
|
|
2590
|
+
case 'vertex':
|
|
2591
|
+
corners[index].x = corners[i2].x + delta1.x + delta2.x;
|
|
2592
|
+
corners[index].y = corners[i2].y + delta1.y + delta2.y;
|
|
2593
|
+
corners[i1].x = corners[i2].x + delta1.x;
|
|
2594
|
+
corners[i1].y = corners[i2].y + delta1.y;
|
|
2595
|
+
corners[i3].x = corners[i2].x + delta2.x;
|
|
2596
|
+
corners[i3].y = corners[i2].y + delta2.y;
|
|
2597
|
+
break;
|
|
2598
|
+
case 'edge':
|
|
2599
|
+
edge = {
|
|
2600
|
+
x: (corners[i2].x + corners[i3].x) * 0.5,
|
|
2601
|
+
y: (corners[i2].y + corners[i3].y) * 0.5
|
|
2602
|
+
};
|
|
2603
|
+
corners[i2].x = edge.x + delta2.x / 2;
|
|
2604
|
+
corners[i2].y = edge.y + delta2.y / 2;
|
|
2605
|
+
corners[index].x = edge.x + delta1.x - delta2.x / 2;
|
|
2606
|
+
corners[index].y = edge.y + delta1.y - delta2.y / 2;
|
|
2607
|
+
corners[i1].x = edge.x + delta1.x + delta2.x / 2;
|
|
2608
|
+
corners[i1].y = edge.y + delta1.y + delta2.y / 2;
|
|
2609
|
+
corners[i3].x = edge.x - delta2.x / 2;
|
|
2610
|
+
corners[i3].y = edge.y - delta2.y / 2;
|
|
2611
|
+
break;
|
|
2612
|
+
}
|
|
2613
|
+
} else {
|
|
2614
|
+
/* Not in edit vertex or edge mode */
|
|
2615
|
+
const area = Math.abs((pos.x - origin.x) * (pos.y - origin.y));
|
|
2616
|
+
ratios.forEach((ratio) => {
|
|
2617
|
+
const width = (area * ratio) ** 0.5;
|
|
2618
|
+
const adjusted = {
|
|
2619
|
+
x: origin.x + Math.sign(pos.x - origin.x) * width,
|
|
2620
|
+
y: origin.y + Math.sign(pos.y - origin.y) * width / ratio
|
|
2621
|
+
};
|
|
2622
|
+
const score = (adjusted.x - pos.x) ** 2 + (adjusted.y - pos.y) ** 2;
|
|
2623
|
+
if (best === undefined || score < best) {
|
|
2624
|
+
best = score;
|
|
2625
|
+
newpos = adjusted;
|
|
2626
|
+
}
|
|
2627
|
+
});
|
|
2628
|
+
corners[0].y = corners[1].y = origin.y;
|
|
2629
|
+
corners[0].x = corners[3].x = origin.x;
|
|
2630
|
+
corners[1].x = corners[2].x = newpos.x;
|
|
2631
|
+
corners[2].y = corners[3].y = newpos.y;
|
|
2632
|
+
}
|
|
2633
|
+
return {
|
|
2634
|
+
corners: corners,
|
|
2635
|
+
origin: origin,
|
|
2636
|
+
pos: newpos
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
return constraintFunction;
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2380
2643
|
module.exports = {
|
|
2381
2644
|
state: annotationState,
|
|
2382
2645
|
actionOwner: annotationActionOwner,
|
|
@@ -2385,6 +2648,10 @@ module.exports = {
|
|
|
2385
2648
|
pointAnnotation: pointAnnotation,
|
|
2386
2649
|
polygonAnnotation: polygonAnnotation,
|
|
2387
2650
|
rectangleAnnotation: rectangleAnnotation,
|
|
2651
|
+
squareAnnotation: squareAnnotation,
|
|
2652
|
+
ellipseAnnotation: ellipseAnnotation,
|
|
2653
|
+
circleAnnotation: circleAnnotation,
|
|
2388
2654
|
_editHandleFeatureLevel: editHandleFeatureLevel,
|
|
2389
|
-
defaultEditHandleStyle: defaultEditHandleStyle
|
|
2655
|
+
defaultEditHandleStyle: defaultEditHandleStyle,
|
|
2656
|
+
constrainAspectRatio: constrainAspectRatio
|
|
2390
2657
|
};
|
package/src/annotationLayer.js
CHANGED
|
@@ -529,10 +529,12 @@ var annotationLayer = function (arg) {
|
|
|
529
529
|
* {@link geo.listAnnotations}.
|
|
530
530
|
* @param {geo.annotation} [editAnnotation] If `arg === this.modes.edit`,
|
|
531
531
|
* this is the annotation that should be edited.
|
|
532
|
+
* @param {object} [options] Additional options to pass when creating an
|
|
533
|
+
* annotation.
|
|
532
534
|
* @returns {string|null|this} The current mode or the layer.
|
|
533
535
|
* @fires geo.event.annotation.mode
|
|
534
536
|
*/
|
|
535
|
-
this.mode = function (arg, editAnnotation) {
|
|
537
|
+
this.mode = function (arg, editAnnotation, options) {
|
|
536
538
|
if (arg === undefined) {
|
|
537
539
|
return m_mode;
|
|
538
540
|
}
|
|
@@ -562,32 +564,21 @@ var annotationLayer = function (arg) {
|
|
|
562
564
|
}
|
|
563
565
|
m_this.currentAnnotation = null;
|
|
564
566
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
case 'line':
|
|
572
|
-
createAnnotation = geo_annotation.lineAnnotation;
|
|
573
|
-
break;
|
|
574
|
-
case 'point':
|
|
575
|
-
createAnnotation = geo_annotation.pointAnnotation;
|
|
576
|
-
break;
|
|
577
|
-
case 'polygon':
|
|
578
|
-
createAnnotation = geo_annotation.polygonAnnotation;
|
|
579
|
-
break;
|
|
580
|
-
case 'rectangle':
|
|
581
|
-
createAnnotation = geo_annotation.rectangleAnnotation;
|
|
582
|
-
break;
|
|
567
|
+
if (m_mode === m_this.modes.edit) {
|
|
568
|
+
m_this.currentAnnotation = editAnnotation;
|
|
569
|
+
m_this.currentAnnotation.state(geo_annotation.state.edit);
|
|
570
|
+
m_this.modified();
|
|
571
|
+
} else if (registry.registries.annotations[m_mode]) {
|
|
572
|
+
createAnnotation = registry.registries.annotations[m_mode].func;
|
|
583
573
|
}
|
|
584
574
|
m_this.map().interactor().removeAction(
|
|
585
575
|
undefined, undefined, geo_annotation.actionOwner);
|
|
586
576
|
if (createAnnotation) {
|
|
587
|
-
|
|
577
|
+
options = $.extend({}, options || {}, {
|
|
588
578
|
state: geo_annotation.state.create,
|
|
589
579
|
layer: m_this
|
|
590
580
|
});
|
|
581
|
+
m_this.currentAnnotation = createAnnotation(options);
|
|
591
582
|
m_this.addAnnotation(m_this.currentAnnotation, null);
|
|
592
583
|
actions = m_this.currentAnnotation.actions(geo_annotation.state.create);
|
|
593
584
|
$.each(actions, function (idx, action) {
|
|
@@ -700,7 +691,7 @@ var annotationLayer = function (arg) {
|
|
|
700
691
|
options.style = options.style || {};
|
|
701
692
|
options.labelStyle = options.labelStyle || {};
|
|
702
693
|
delete options.annotationType;
|
|
703
|
-
// the geoJSON reader can
|
|
694
|
+
// the geoJSON reader can emit line, polygon, point, and marker
|
|
704
695
|
switch (feature.featureType) {
|
|
705
696
|
case 'line':
|
|
706
697
|
position = feature.line()(data, data_idx);
|
|
@@ -725,6 +716,9 @@ var annotationLayer = function (arg) {
|
|
|
725
716
|
}
|
|
726
717
|
}
|
|
727
718
|
break;
|
|
719
|
+
case 'marker':
|
|
720
|
+
position = data.geometry.coordinates[0].slice(0, 4);
|
|
721
|
+
break;
|
|
728
722
|
case 'point':
|
|
729
723
|
position = [feature.position()(data, data_idx)];
|
|
730
724
|
break;
|
|
@@ -984,8 +978,9 @@ var annotationLayer = function (arg) {
|
|
|
984
978
|
$.each([
|
|
985
979
|
'closed', 'fill', 'fillColor', 'fillOpacity', 'line',
|
|
986
980
|
'lineCap', 'lineJoin', 'polygon', 'position', 'radius',
|
|
987
|
-
'
|
|
988
|
-
'
|
|
981
|
+
'radiusIncludesStroke', 'rotateWithMap', 'rotation',
|
|
982
|
+
'scaleWithZoom', 'stroke', 'strokeColor', 'strokeOffset',
|
|
983
|
+
'strokeOpacity', 'strokeWidth', 'symbolValue', 'uniformPolygon'
|
|
989
984
|
], function (keyidx, key) {
|
|
990
985
|
var origFunc;
|
|
991
986
|
if (feature.style()[key] !== undefined) {
|
package/src/geojsonReader.js
CHANGED
|
@@ -31,6 +31,8 @@ var geojsonReader = function (arg) {
|
|
|
31
31
|
|
|
32
32
|
var $ = require('jquery');
|
|
33
33
|
var convertColor = require('./util').convertColor;
|
|
34
|
+
var markerFeature = require('./markerFeature');
|
|
35
|
+
var transform = require('./transform');
|
|
34
36
|
|
|
35
37
|
var m_this = this,
|
|
36
38
|
m_options = {
|
|
@@ -309,6 +311,22 @@ var geojsonReader = function (arg) {
|
|
|
309
311
|
*/
|
|
310
312
|
this.read = function (file, done, progress) {
|
|
311
313
|
var promise = new Promise(function (resolve, reject) {
|
|
314
|
+
/**
|
|
315
|
+
* Check if a feature is a circle or an ellipse.
|
|
316
|
+
*
|
|
317
|
+
* @param {geojson.object} f A geojson feature.
|
|
318
|
+
* @returns {boolean} true if this should be rendered as an ellipse or
|
|
319
|
+
* circle.
|
|
320
|
+
*/
|
|
321
|
+
function _isEllipse(f) {
|
|
322
|
+
if (f.geometry.type !== 'Polygon' || f.geometry.coordinates.length !== 1 || f.geometry.coordinates[0].length !== 5) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
return (
|
|
326
|
+
(f.properties || {}).annotationType === 'ellipse' ||
|
|
327
|
+
(f.properties || {}).annotationType === 'circle');
|
|
328
|
+
}
|
|
329
|
+
|
|
312
330
|
/**
|
|
313
331
|
* Given a parsed GeoJSON object, convert it into features on the
|
|
314
332
|
* reader's layer.
|
|
@@ -384,7 +402,7 @@ var geojsonReader = function (arg) {
|
|
|
384
402
|
}
|
|
385
403
|
|
|
386
404
|
// process polygons
|
|
387
|
-
const polygons = features.filter(f => f.geometry.type === 'Polygon');
|
|
405
|
+
const polygons = features.filter(f => f.geometry.type === 'Polygon' && !_isEllipse(f));
|
|
388
406
|
if (polygons.length) {
|
|
389
407
|
feature = m_this.layer().createFeature('polygon');
|
|
390
408
|
if (feature) {
|
|
@@ -410,6 +428,55 @@ var geojsonReader = function (arg) {
|
|
|
410
428
|
allFeatures.push(feature);
|
|
411
429
|
}
|
|
412
430
|
}
|
|
431
|
+
// handle ellipses and circle
|
|
432
|
+
const ellipses = features.filter(_isEllipse);
|
|
433
|
+
if (ellipses.length) {
|
|
434
|
+
feature = m_this.layer().createFeature('marker');
|
|
435
|
+
if (feature) {
|
|
436
|
+
ellipses.forEach((d) => {
|
|
437
|
+
const map = m_this.layer().map();
|
|
438
|
+
const coord = transform.transformCoordinates(map.ingcs(), map.gcs(), d.geometry.coordinates[0]);
|
|
439
|
+
const w = ((coord[0][0] - coord[1][0]) ** 2 + (coord[0][1] - coord[1][1]) ** 2) ** 0.5;
|
|
440
|
+
const h = ((coord[0][0] - coord[3][0]) ** 2 + (coord[0][1] - coord[3][1]) ** 2) ** 0.5;
|
|
441
|
+
const radius = Math.max(w, h) / 2 / map.unitsPerPixel(0);
|
|
442
|
+
const aspect = w ? h / w : 1e20;
|
|
443
|
+
const rotation = -Math.atan2(coord[1][1] - coord[0][1], coord[1][0] - coord[0][0]);
|
|
444
|
+
const pos = transform.transformCoordinates(map.gcs(), map.ingcs(), {
|
|
445
|
+
x: (coord[0][0] + coord[1][0] + coord[2][0] + coord[3][0]) / 4,
|
|
446
|
+
y: (coord[0][1] + coord[1][1] + coord[2][1] + coord[3][1]) / 4
|
|
447
|
+
});
|
|
448
|
+
d._props = {
|
|
449
|
+
pos: pos,
|
|
450
|
+
radius: radius,
|
|
451
|
+
aspect: aspect,
|
|
452
|
+
rotation: rotation
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
feature
|
|
456
|
+
.data(ellipses)
|
|
457
|
+
.position((d) => d._props.pos)
|
|
458
|
+
// create an object with each property in m_options.polygonStyle,
|
|
459
|
+
// mapping the values through the _style function.
|
|
460
|
+
.style(
|
|
461
|
+
[{}].concat(Object.keys(m_options.polygonStyle)).reduce(
|
|
462
|
+
(styleObj, key) => ({
|
|
463
|
+
[key]: ellipses.some(d => d.properties && d.properties[key] !== undefined) ?
|
|
464
|
+
m_this._style(key, m_options.polygonStyle[key]) :
|
|
465
|
+
m_options.polygonStyle[key],
|
|
466
|
+
radius: (d) => d._props.radius,
|
|
467
|
+
radiusIncludesStroke: false,
|
|
468
|
+
symbolValue: (d) => d._props.aspect,
|
|
469
|
+
rotation: (d) => d._props.rotation,
|
|
470
|
+
strokeOffset: 0,
|
|
471
|
+
rotateWithMap: true,
|
|
472
|
+
scaleWithZoom: markerFeature.scaleMode.fill,
|
|
473
|
+
...styleObj
|
|
474
|
+
}
|
|
475
|
+
))
|
|
476
|
+
);
|
|
477
|
+
allFeatures.push(feature);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
413
480
|
if (done) {
|
|
414
481
|
done(allFeatures);
|
|
415
482
|
}
|
package/src/map.js
CHANGED
|
@@ -569,8 +569,7 @@ var map = function (arg) {
|
|
|
569
569
|
};
|
|
570
570
|
|
|
571
571
|
/**
|
|
572
|
-
*
|
|
573
|
-
* current center. Uses bare objects {x: 0, y: 0}.
|
|
572
|
+
* Get or set the center of the map in the given geographic coordinates.
|
|
574
573
|
*
|
|
575
574
|
* @param {geo.geoPosition} coordinates If specified, the new center of the
|
|
576
575
|
* map.
|