geojs 1.8.4 → 1.8.7

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.
@@ -1,15 +1,9 @@
1
1
  var $ = require('jquery');
2
- var inherit = require('./inherit');
3
- var geo_event = require('./event');
4
- var geo_action = require('./action');
5
- var transform = require('./transform');
6
- var util = require('./util');
7
- var registerAnnotation = require('./registry').registerAnnotation;
8
- var lineFeature = require('./lineFeature');
9
- var markerFeature = require('./markerFeature');
10
- var pointFeature = require('./pointFeature');
11
- var polygonFeature = require('./polygonFeature');
12
- var textFeature = require('./textFeature');
2
+ var geo_event = require('../event');
3
+ var geo_action = require('../action');
4
+ var transform = require('../transform');
5
+ var util = require('../util');
6
+ var textFeature = require('../textFeature');
13
7
 
14
8
  var annotationId = 0;
15
9
 
@@ -1267,1358 +1261,17 @@ function continuousVerticesProcessAction(m_this, evt, name) {
1267
1261
  }
1268
1262
  }
1269
1263
 
1270
- /**
1271
- * Rectangle annotation specification. Extends {@link geo.annotation.spec}.
1272
- *
1273
- * @typedef {object} geo.rectangleAnnotation.spec
1274
- * @extends geo.annotation.spec
1275
- * @property {geo.geoPosition[]} [corners] A list of four corners in map gcs
1276
- * coordinates. These must be in order around the perimeter of the
1277
- * rectangle (in either direction).
1278
- * @property {geo.geoPosition[]} [coordinates] An alternate name for `corners`.
1279
- * @property {geo.polygonFeature.styleSpec} [style] The style to apply to a
1280
- * finished rectangle. This uses styles for {@link geo.polygonFeature}.
1281
- * @property {geo.polygonFeature.styleSpec} [editStyle] The style to apply to a
1282
- * rectangle in edit mode.
1283
- * @property {number|number[]|function} [constraint] If specified, an aspect
1284
- * ratio or list of aspect ratios to constraint the rectangle to. If a
1285
- * function, a selection constraint function to call to adjust the
1286
- * rectangle.
1287
- */
1288
-
1289
- /**
1290
- * Rectangle annotation class.
1291
- *
1292
- * Rectangles are always rendered as polygons. This could be changed -- if no
1293
- * stroke is specified, the quad feature would be sufficient and work on more
1294
- * renderers.
1295
- *
1296
- * @class
1297
- * @alias geo.rectangleAnnotation
1298
- * @extends geo.annotation
1299
- *
1300
- * @param {geo.rectangleAnnotation.spec?} [args] Options for the annotation.
1301
- * @param {string} [annotationName='rectangle'] Override the annotation name.
1302
- */
1303
- var rectangleAnnotation = function (args, annotationName) {
1304
- 'use strict';
1305
- if (!(this instanceof rectangleAnnotation)) {
1306
- return new rectangleAnnotation(args, annotationName);
1307
- }
1308
-
1309
- args = $.extend(true, {}, {
1310
- style: {
1311
- fill: true,
1312
- fillColor: {r: 0, g: 1, b: 0},
1313
- fillOpacity: 0.25,
1314
- polygon: function (d) { return d.polygon; },
1315
- stroke: true,
1316
- strokeColor: {r: 0, g: 0, b: 0},
1317
- strokeOpacity: 1,
1318
- strokeWidth: 3,
1319
- uniformPolygon: true
1320
- },
1321
- highlightStyle: {
1322
- fillColor: {r: 0, g: 1, b: 1},
1323
- fillOpacity: 0.5,
1324
- strokeWidth: 5
1325
- },
1326
- createStyle: {
1327
- fillColor: {r: 0.3, g: 0.3, b: 0.3},
1328
- fillOpacity: 0.25,
1329
- strokeColor: {r: 0, g: 0, b: 1}
1330
- }
1331
- }, args || {});
1332
- args.corners = args.corners || args.coordinates || [];
1333
- delete args.coordinates;
1334
- annotation.call(this, annotationName || 'rectangle', args);
1335
-
1336
- var m_this = this,
1337
- s_actions = this.actions,
1338
- s_processEditAction = this.processEditAction;
1339
-
1340
- /**
1341
- * Return actions needed for the specified state of this annotation.
1342
- *
1343
- * @param {string} [state] The state to return actions for. Defaults to
1344
- * the current state.
1345
- * @returns {geo.actionRecord[]} A list of actions.
1346
- */
1347
- this.actions = function (state) {
1348
- if (!state) {
1349
- state = m_this.state();
1350
- }
1351
- switch (state) {
1352
- case annotationState.create:
1353
- return [{
1354
- action: geo_action.annotation_rectangle,
1355
- name: 'rectangle create',
1356
- owner: annotationActionOwner,
1357
- input: 'left',
1358
- modifiers: {shift: false, ctrl: false},
1359
- selectionRectangle: true,
1360
- selectionConstraint: this._selectionConstraint
1361
- }];
1362
- default:
1363
- return s_actions.apply(m_this, arguments);
1364
- }
1365
- };
1366
-
1367
- /**
1368
- * Process any actions for this annotation.
1369
- *
1370
- * @param {geo.event} evt The action event.
1371
- * @returns {boolean|string} `true` to update the annotation, `'done'` if the
1372
- * annotation was completed (changed from create to done state),
1373
- * `'remove'` if the annotation should be removed, falsy to not update
1374
- * anything.
1375
- */
1376
- this.processAction = function (evt) {
1377
- var layer = m_this.layer();
1378
- if (m_this.state() !== annotationState.create || !layer ||
1379
- evt.event !== geo_event.actionselection ||
1380
- evt.state.action !== geo_action.annotation_rectangle) {
1381
- return;
1382
- }
1383
- var map = layer.map(),
1384
- corners = [
1385
- /* Keep in map gcs, not interface gcs to avoid wrapping issues */
1386
- map.displayToGcs({x: evt.lowerLeft.x, y: evt.lowerLeft.y}, null),
1387
- map.displayToGcs({x: evt.lowerLeft.x, y: evt.upperRight.y}, null),
1388
- map.displayToGcs({x: evt.upperRight.x, y: evt.upperRight.y}, null),
1389
- map.displayToGcs({x: evt.upperRight.x, y: evt.lowerLeft.y}, null)
1390
- ];
1391
- if (this._selectionConstraint && evt.mouse && evt.state.origin) {
1392
- this._selectionConstraint(evt.mouse.mapgcs, evt.state.origin.mapgcs, corners);
1393
- }
1394
- /* Don't keep rectangles that have nearly zero area in display pixels */
1395
- if (layer.displayDistance(corners[0], null, corners[1], null) *
1396
- layer.displayDistance(corners[0], null, corners[3], null) < 0.01) {
1397
- return 'remove';
1398
- }
1399
- m_this.options('corners', corners);
1400
- m_this.state(annotationState.done);
1401
- return 'done';
1402
- };
1403
-
1404
- /**
1405
- * Get a list of renderable features for this annotation.
1406
- *
1407
- * @returns {array} An array of features.
1408
- */
1409
- this.features = function () {
1410
- var opt = m_this.options(),
1411
- state = m_this.state(),
1412
- features;
1413
- features = [];
1414
- if (opt.corners && opt.corners.length >= 4) {
1415
- features = [{
1416
- polygon: {
1417
- polygon: opt.corners,
1418
- style: m_this.styleForState(state)
1419
- }
1420
- }];
1421
- }
1422
- if (state === annotationState.edit) {
1423
- m_this._addEditHandles(features, opt.corners);
1424
- }
1425
- return features;
1426
- };
1427
-
1428
- /**
1429
- * Get and optionally set coordinates associated with this annotation in the
1430
- * map gcs coordinate system.
1431
- *
1432
- * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
1433
- * to set.
1434
- * @returns {geo.geoPosition[]} The current array of coordinates.
1435
- */
1436
- this._coordinates = function (coordinates) {
1437
- if (coordinates && coordinates.length >= 4) {
1438
- m_this.options('corners', coordinates.slice(0, 4));
1439
- /* Should we ensure that the four points form a rectangle in the current
1440
- * projection, though this might not be rectangular in another gcs? */
1441
- }
1442
- return m_this.options('corners');
1443
- };
1444
-
1445
- this._coordinateOption = 'corners';
1446
-
1447
- /**
1448
- * Return the coordinates to be stored in a geojson geometry object.
1449
- *
1450
- * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
1451
- * gcs, `null` to use the map gcs, or any other transform.
1452
- * @returns {array} An array of flattened coordinates in the interface gcs
1453
- * coordinate system. `undefined` if this annotation is incomplete.
1454
- */
1455
- this._geojsonCoordinates = function (gcs) {
1456
- var src = m_this.coordinates(gcs);
1457
- if (!src || m_this.state() === annotationState.create || src.length < 4) {
1458
- return;
1459
- }
1460
- var coord = [];
1461
- for (var i = 0; i < 4; i += 1) {
1462
- coord.push([src[i].x, src[i].y]);
1463
- }
1464
- coord.push([src[0].x, src[0].y]);
1465
- return [coord];
1466
- };
1467
-
1468
- /**
1469
- * Return the geometry type that is used to store this annotation in geojson.
1470
- *
1471
- * @returns {string} A geojson geometry type.
1472
- */
1473
- this._geojsonGeometryType = function () {
1474
- return 'Polygon';
1475
- };
1476
-
1477
- /**
1478
- * Return a list of styles that should be preserved in a geojson
1479
- * representation of the annotation.
1480
- *
1481
- * @returns {string[]} A list of style names to store.
1482
- */
1483
- this._geojsonStyles = function () {
1484
- return [
1485
- 'fill', 'fillColor', 'fillOpacity', 'lineCap', 'lineJoin', 'stroke',
1486
- 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth'];
1487
- };
1488
-
1489
- /**
1490
- * Set three corners based on an initial corner and a mouse event.
1491
- *
1492
- * @param {geo.geoPosition} corners An array of four corners to update.
1493
- * @param {geo.event} evt The mouse move event.
1494
- */
1495
- this._setCornersFromMouse = function (corners, evt) {
1496
- var map = m_this.layer().map(),
1497
- c0 = map.gcsToDisplay({x: corners[0].x, y: corners[0].y}, null),
1498
- c2 = map.gcsToDisplay(evt.mapgcs, null),
1499
- c1 = {x: c2.x, y: c0.y},
1500
- c3 = {x: c0.x, y: c2.y};
1501
- corners[2] = $.extend({}, evt.mapgcs);
1502
- corners[1] = map.displayToGcs(c1, null);
1503
- corners[3] = map.displayToGcs(c3, null);
1504
- if (this._selectionConstraint) {
1505
- this._selectionConstraint(evt.mapgcs, corners[0], corners);
1506
- }
1507
- };
1508
-
1509
- /**
1510
- * Handle a mouse move on this annotation.
1511
- *
1512
- * @param {geo.event} evt The mouse move event.
1513
- * @returns {boolean} Truthy to update the annotation, falsy to not
1514
- * update anything.
1515
- */
1516
- this.mouseMove = function (evt) {
1517
- if (m_this.state() !== annotationState.create) {
1518
- return;
1519
- }
1520
- var corners = m_this.options('corners');
1521
- if (corners.length) {
1522
- m_this._setCornersFromMouse(corners, evt);
1523
- return true;
1524
- }
1525
- };
1526
-
1527
- /**
1528
- * Handle a mouse click on this annotation. If the event is processed,
1529
- * evt.handled should be set to `true` to prevent further processing.
1530
- *
1531
- * @param {geo.event} evt The mouse click event.
1532
- * @returns {boolean|string} `true` to update the annotation, `'done'` if
1533
- * the annotation was completed (changed from create to done state),
1534
- * `'remove'` if the annotation should be removed, falsy to not update
1535
- * anything.
1536
- */
1537
- this.mouseClick = function (evt) {
1538
- var layer = m_this.layer();
1539
- if (m_this.state() !== annotationState.create || !layer) {
1540
- return;
1541
- }
1542
- if (!evt.buttonsDown.left && !evt.buttonsDown.right) {
1543
- return;
1544
- }
1545
- var corners = m_this.options('corners');
1546
- if (evt.buttonsDown.right && !corners.length) {
1547
- return;
1548
- }
1549
- evt.handled = true;
1550
- if (corners.length) {
1551
- m_this._setCornersFromMouse(corners, evt);
1552
- /* Don't keep rectangles that have nearly zero area in display pixels */
1553
- if (layer.displayDistance(corners[0], null, corners[1], null) *
1554
- layer.displayDistance(corners[0], null, corners[3], null) < 0.01) {
1555
- return 'remove';
1556
- }
1557
- m_this.state(annotationState.done);
1558
- return 'done';
1559
- }
1560
- if (evt.buttonsDown.left) {
1561
- corners.push($.extend({}, evt.mapgcs));
1562
- corners.push($.extend({}, evt.mapgcs));
1563
- corners.push($.extend({}, evt.mapgcs));
1564
- corners.push($.extend({}, evt.mapgcs));
1565
- return true;
1566
- }
1567
- };
1568
-
1569
- /**
1570
- * Process any edit actions for this annotation.
1571
- *
1572
- * @param {geo.event} evt The action event.
1573
- * @returns {boolean|string} `true` to update the annotation, falsy to not
1574
- * update anything.
1575
- */
1576
- this.processEditAction = function (evt) {
1577
- var start = m_this._editHandle.startCoordinates,
1578
- delta = {
1579
- x: evt.mouse.mapgcs.x - evt.state.origin.mapgcs.x,
1580
- y: evt.mouse.mapgcs.y - evt.state.origin.mapgcs.y
1581
- },
1582
- type = m_this._editHandle.handle.type,
1583
- index = m_this._editHandle.handle.index,
1584
- ang = [
1585
- Math.atan2(start[1].y - start[0].y, start[1].x - start[0].x),
1586
- Math.atan2(start[2].y - start[1].y, start[2].x - start[1].x),
1587
- Math.atan2(start[3].y - start[2].y, start[3].x - start[2].x),
1588
- Math.atan2(start[0].y - start[3].y, start[0].x - start[3].x)
1589
- ],
1590
- corners, delta1, delta2, ang1, ang2;
1591
- // an angle can be zero because it is horizontal or undefined. If opposite
1592
- // angles are both zero, this is a degenerate rectangle (a line or a point)
1593
- if (!ang[0] && !ang[1] && !ang[2] && !ang[3]) {
1594
- ang[1] = Math.PI / 2;
1595
- ang[2] = Math.PI;
1596
- ang[3] = -Math.PI / 2;
1597
- }
1598
- if (!ang[0] && !ang[2]) {
1599
- ang[0] = ang[1] - Math.PI / 2;
1600
- ang[2] = ang[1] + Math.PI / 2;
1601
- }
1602
- if (!ang[1] && !ang[3]) {
1603
- ang[1] = ang[2] - Math.PI / 2;
1604
- ang[3] = ang[2] + Math.PI / 2;
1605
- }
1606
- switch (type) {
1607
- case 'vertex':
1608
- corners = start.map(function (elem) {
1609
- return {x: elem.x, y: elem.y};
1610
- });
1611
- ang1 = ang[(index + 1) % 4];
1612
- delta1 = {
1613
- x: (delta.x * Math.cos(ang1) + delta.y * Math.sin(ang1)) * Math.cos(ang1),
1614
- y: (delta.y * Math.sin(ang1) + delta.x * Math.cos(ang1)) * Math.sin(ang1)
1615
- };
1616
- ang2 = ang[index];
1617
- delta2 = {
1618
- x: (delta.x * Math.cos(ang2) + delta.y * Math.sin(ang2)) * Math.cos(ang2),
1619
- y: (delta.y * Math.sin(ang2) + delta.x * Math.cos(ang2)) * Math.sin(ang2)
1620
- };
1621
- corners[index].x += delta.x;
1622
- corners[index].y += delta.y;
1623
- corners[(index + 1) % 4].x += delta1.x;
1624
- corners[(index + 1) % 4].y += delta1.y;
1625
- corners[(index + 3) % 4].x += delta2.x;
1626
- corners[(index + 3) % 4].y += delta2.y;
1627
- if (this._selectionConstraint) {
1628
- corners = this._selectionConstraint(evt.mouse.mapgcs, evt.state.origin.mapgcs, corners, 'vertex', ang, index).corners;
1629
- }
1630
- m_this.options('corners', corners);
1631
- return true;
1632
- case 'edge':
1633
- corners = start.map(function (elem) {
1634
- return {x: elem.x, y: elem.y};
1635
- });
1636
- ang1 = ang[(index + 1) % 4];
1637
- delta = {
1638
- x: (delta.x * Math.cos(ang1) + delta.y * Math.sin(ang1)) * Math.cos(ang1),
1639
- y: (delta.y * Math.sin(ang1) + delta.x * Math.cos(ang1)) * Math.sin(ang1)
1640
- };
1641
- corners[index].x += delta.x;
1642
- corners[index].y += delta.y;
1643
- corners[(index + 1) % 4].x += delta.x;
1644
- corners[(index + 1) % 4].y += delta.y;
1645
- if (this._selectionConstraint) {
1646
- corners = this._selectionConstraint(evt.mouse.mapgcs, evt.state.origin.mapgcs, corners, 'edge', ang, index).corners;
1647
- }
1648
- m_this.options('corners', corners);
1649
- return true;
1650
- }
1651
- return s_processEditAction.apply(m_this, arguments);
1652
- };
1653
- };
1654
- inherit(rectangleAnnotation, annotation);
1655
-
1656
- var rectangleRequiredFeatures = {};
1657
- rectangleRequiredFeatures[polygonFeature.capabilities.feature] = true;
1658
- registerAnnotation('rectangle', rectangleAnnotation, rectangleRequiredFeatures);
1659
-
1660
- /**
1661
- * Square annotation class.
1662
- *
1663
- * Squares are a subset of rectangles with a fixed aspect ratio.
1664
- *
1665
- * @class
1666
- * @alias geo.squareAnnotation
1667
- * @extends geo.annotation
1668
- *
1669
- * @param {geo.squareAnnotation.spec?} [args] Options for the annotation.
1670
- * @param {string} [annotationName='square'] Override the annotation name.
1671
- */
1672
- var squareAnnotation = function (args, annotationName) {
1673
- 'use strict';
1674
- args = $.extend({}, args, {constraint: 1});
1675
- if (!(this instanceof squareAnnotation)) {
1676
- return new squareAnnotation(args, annotationName);
1677
- }
1678
- rectangleAnnotation.call(this, args, annotationName || 'square');
1679
- };
1680
- inherit(squareAnnotation, rectangleAnnotation);
1681
- var squareRequiredFeatures = {};
1682
- squareRequiredFeatures[polygonFeature.capabilities.feature] = true;
1683
- registerAnnotation('square', squareAnnotation, squareRequiredFeatures);
1684
-
1685
- /**
1686
- * Ellipse annotation class.
1687
- *
1688
- * Ellipses are always rendered as markers.
1689
- *
1690
- * @class
1691
- * @alias geo.ellipseAnnotation
1692
- * @extends geo.annotation
1693
- *
1694
- * @param {geo.ellipseAnnotation.spec?} [args] Options for the annotation.
1695
- * @param {string} [annotationName='ellipse'] Override the annotation name.
1696
- */
1697
- var ellipseAnnotation = function (args, annotationName) {
1698
- 'use strict';
1699
- if (!(this instanceof ellipseAnnotation)) {
1700
- return new ellipseAnnotation(args, annotationName);
1701
- }
1702
-
1703
- rectangleAnnotation.call(this, args, annotationName || 'ellipse');
1704
-
1705
- var m_this = this;
1706
-
1707
- /**
1708
- * Get a list of renderable features for this annotation.
1709
- *
1710
- * @returns {array} An array of features.
1711
- */
1712
- this.features = function () {
1713
- var opt = m_this.options(),
1714
- state = m_this.state(),
1715
- features;
1716
- features = [];
1717
- if (opt.corners && opt.corners.length >= 4) {
1718
- const style = m_this.styleForState(state);
1719
- const w = ((opt.corners[0].x - opt.corners[1].x) ** 2 + (opt.corners[0].y - opt.corners[1].y) ** 2) ** 0.5;
1720
- const h = ((opt.corners[0].x - opt.corners[3].x) ** 2 + (opt.corners[0].y - opt.corners[3].y) ** 2) ** 0.5;
1721
- const radius = Math.max(w, h) / 2 / m_this.layer().map().unitsPerPixel(0);
1722
- const aspect = w ? h / w : 1e20;
1723
- const rotation = -Math.atan2(opt.corners[1].y - opt.corners[0].y, opt.corners[1].x - opt.corners[0].x);
1724
- features = [{
1725
- marker: {
1726
- x: (opt.corners[0].x + opt.corners[1].x + opt.corners[2].x + opt.corners[3].x) / 4,
1727
- y: (opt.corners[0].y + opt.corners[1].y + opt.corners[2].y + opt.corners[3].y) / 4,
1728
- style: $.extend(
1729
- {}, style,
1730
- {
1731
- radius: radius,
1732
- symbolValue: aspect,
1733
- rotation: rotation,
1734
- strokeOffset: 0,
1735
- radiusIncludesStroke: false,
1736
- scaleWithZoom: markerFeature.scaleMode.fill,
1737
- rotateWithMap: true,
1738
- strokeOpacity: style.stroke === false ? 0 : style.strokeOpacity,
1739
- fillOpacity: style.fill === false ? 0 : style.fillOpacity
1740
- })
1741
- }
1742
- }];
1743
- }
1744
- if (state === annotationState.edit) {
1745
- m_this._addEditHandles(features, opt.corners);
1746
- }
1747
- return features;
1748
- };
1749
- };
1750
- inherit(ellipseAnnotation, rectangleAnnotation);
1751
-
1752
- var ellipseRequiredFeatures = {};
1753
- ellipseRequiredFeatures[markerFeature.capabilities.feature] = true;
1754
- registerAnnotation('ellipse', ellipseAnnotation, ellipseRequiredFeatures);
1755
-
1756
- /**
1757
- * Circle annotation class.
1758
- *
1759
- * Circles are a subset of rectangles with a fixed aspect ratio.
1760
- *
1761
- * @class
1762
- * @alias geo.circleAnnotation
1763
- * @extends geo.annotation
1764
- *
1765
- * @param {geo.circleAnnotation.spec?} [args] Options for the annotation.
1766
- * @param {string} [annotationName='circle'] Override the annotation name.
1767
- */
1768
- var circleAnnotation = function (args, annotationName) {
1769
- 'use strict';
1770
- args = $.extend({}, args, {constraint: 1});
1771
- if (!(this instanceof circleAnnotation)) {
1772
- return new circleAnnotation(args, annotationName);
1773
- }
1774
- ellipseAnnotation.call(this, args, annotationName || 'circle');
1775
- };
1776
- inherit(circleAnnotation, ellipseAnnotation);
1777
- var circleRequiredFeatures = {};
1778
- circleRequiredFeatures[markerFeature.capabilities.feature] = true;
1779
- registerAnnotation('circle', circleAnnotation, circleRequiredFeatures);
1780
-
1781
- /**
1782
- * Polygon annotation specification. Extends {@link geo.annotation.spec}.
1783
- *
1784
- * @typedef {object} geo.polygonAnnotation.spec
1785
- * @extends geo.annotation.spec
1786
- * @property {geo.geoPosition[]} [vertices] A list of vertices in map gcs
1787
- * coordinates. These must be in order around the perimeter of the polygon
1788
- * (in either direction).
1789
- * @property {geo.geoPosition[]} [coordinates] An alternate name for
1790
- * `vertices`.
1791
- * @property {geo.polygonFeature.styleSpec} [style] The style to apply to a
1792
- * finished polygon. This uses styles for {@link geo.polygonFeature}.
1793
- * @property {geo.polygonFeature.styleSpec} [editStyle] The style to apply to ai
1794
- * polygon in edit mode.
1795
- */
1796
-
1797
- /**
1798
- * Polygon annotation class
1799
- *
1800
- * When complete, polygons are rendered as polygons. During creation they are
1801
- * rendered as lines and polygons.
1802
- *
1803
- * @class
1804
- * @alias geo.polygonAnnotation
1805
- * @extends geo.annotation
1806
- *
1807
- * @param {geo.polygonAnnotation.spec?} [args] Options for the annotation.
1808
- */
1809
- var polygonAnnotation = function (args) {
1810
- 'use strict';
1811
- if (!(this instanceof polygonAnnotation)) {
1812
- return new polygonAnnotation(args);
1813
- }
1814
-
1815
- args = $.extend(true, {}, {
1816
- style: {
1817
- fill: true,
1818
- fillColor: {r: 0, g: 1, b: 0},
1819
- fillOpacity: 0.25,
1820
- polygon: function (d) { return d.polygon; },
1821
- stroke: true,
1822
- strokeColor: {r: 0, g: 0, b: 0},
1823
- strokeOpacity: 1,
1824
- strokeWidth: 3,
1825
- uniformPolygon: true
1826
- },
1827
- highlightStyle: {
1828
- fillColor: {r: 0, g: 1, b: 1},
1829
- fillOpacity: 0.5,
1830
- strokeWidth: 5
1831
- },
1832
- createStyle: {
1833
- closed: false,
1834
- fillColor: {r: 0.3, g: 0.3, b: 0.3},
1835
- fillOpacity: 0.25,
1836
- line: function (d) {
1837
- const coord = m_this._coordinates();
1838
- /* Return an array that has the same number of items as we have
1839
- * vertices. */
1840
- return Array.apply(null, Array((coord.outer || coord).length)).map(
1841
- function () { return d; });
1842
- },
1843
- position: function (d, i) {
1844
- if (d.x !== undefined) {
1845
- return d.x;
1846
- }
1847
- return m_this.options('vertices')[i];
1848
- },
1849
- stroke: false,
1850
- strokeColor: {r: 0, g: 0, b: 1}
1851
- }
1852
- }, args || {});
1853
- args.vertices = args.vertices || args.coordinates || [];
1854
- delete args.coordinates;
1855
- annotation.call(this, 'polygon', args);
1856
-
1857
- var m_this = this,
1858
- s_actions = this.actions;
1859
-
1860
- /**
1861
- * Get a list of renderable features for this annotation. When the polygon
1862
- * is done, this is just a single polygon. During creation this can be a
1863
- * polygon and line at z-levels 1 and 2.
1864
- *
1865
- * @returns {array} An array of features.
1866
- */
1867
- this.features = function () {
1868
- var opt = m_this.options(),
1869
- state = m_this.state(),
1870
- style = m_this.styleForState(state),
1871
- features;
1872
- switch (state) {
1873
- case annotationState.create:
1874
- features = [];
1875
- if (opt.vertices && (opt.vertices.outer || opt.vertices.length >= 3)) {
1876
- features[1] = {
1877
- polygon: {
1878
- polygon: opt.vertices,
1879
- style: style
1880
- }
1881
- };
1882
- }
1883
- if (opt.vertices && opt.vertices.length >= 2) {
1884
- features[2] = {
1885
- line: {
1886
- line: opt.vertices,
1887
- style: style
1888
- }
1889
- };
1890
- }
1891
- break;
1892
- default:
1893
- features = [{
1894
- polygon: {
1895
- polygon: opt.vertices,
1896
- style: style
1897
- }
1898
- }];
1899
- if (state === annotationState.edit) {
1900
- m_this._addEditHandles(features, opt.vertices);
1901
- }
1902
- break;
1903
- }
1904
- return features;
1905
- };
1906
-
1907
- /**
1908
- * Get and optionally set coordinates associated with this annotation in the
1909
- * map gcs coordinate system.
1910
- *
1911
- * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
1912
- * to set.
1913
- * @returns {geo.geoPosition[]} The current array of coordinates.
1914
- */
1915
- this._coordinates = function (coordinates) {
1916
- if (coordinates) {
1917
- m_this.options('vertices', coordinates);
1918
- }
1919
- return m_this.options('vertices');
1920
- };
1921
-
1922
- /**
1923
- * Handle a mouse move on this annotation.
1924
- *
1925
- * @param {geo.event} evt The mouse move event.
1926
- * @returns {boolean} Truthy to update the annotation, falsy to not
1927
- * update anything.
1928
- */
1929
- this.mouseMove = function (evt) {
1930
- if (m_this.state() !== annotationState.create) {
1931
- return;
1932
- }
1933
- var vertices = m_this.options('vertices');
1934
- if (vertices.length) {
1935
- vertices[vertices.length - 1] = evt.mapgcs;
1936
- return true;
1937
- }
1938
- };
1939
-
1940
- /**
1941
- * Handle a mouse click on this annotation. If the event is processed,
1942
- * evt.handled should be set to `true` to prevent further processing.
1943
- *
1944
- * @param {geo.event} evt The mouse click event.
1945
- * @returns {boolean|string} `true` to update the annotation, `'done'` if
1946
- * the annotation was completed (changed from create to done state),
1947
- * `'remove'` if the annotation should be removed, falsy to not update
1948
- * anything.
1949
- */
1950
- this.mouseClick = function (evt) {
1951
- var layer = m_this.layer();
1952
- if (m_this.state() !== annotationState.create || !layer) {
1953
- return;
1954
- }
1955
- var end = !!evt.buttonsDown.right, skip;
1956
- if (!evt.buttonsDown.left && !evt.buttonsDown.right) {
1957
- return;
1958
- }
1959
- var vertices = m_this.options('vertices');
1960
- if (evt.buttonsDown.right && !vertices.length) {
1961
- return;
1962
- }
1963
- evt.handled = true;
1964
- if (evt.buttonsDown.left) {
1965
- if (vertices.length) {
1966
- if (vertices.length >= 2 && layer.displayDistance(
1967
- vertices[vertices.length - 2], null, evt.map, 'display') <=
1968
- layer.options('adjacentPointProximity')) {
1969
- skip = true;
1970
- if (m_this._lastClick &&
1971
- evt.time - m_this._lastClick < layer.options('dblClickTime')) {
1972
- end = true;
1973
- }
1974
- } else if (vertices.length >= 2 && layer.displayDistance(
1975
- vertices[0], null, evt.map, 'display') <=
1976
- layer.options('finalPointProximity')) {
1977
- end = true;
1978
- } else {
1979
- vertices[vertices.length - 1] = evt.mapgcs;
1980
- }
1981
- } else {
1982
- vertices.push(evt.mapgcs);
1983
- }
1984
- if (!end && !skip) {
1985
- vertices.push(evt.mapgcs);
1986
- }
1987
- m_this._lastClick = evt.time;
1988
- m_this._lastClickVertexCount = vertices.length;
1989
- }
1990
- if (end) {
1991
- if (vertices.length < 4) {
1992
- return 'remove';
1993
- }
1994
- vertices.pop();
1995
- m_this.state(annotationState.done);
1996
- return 'done';
1997
- }
1998
- return !skip;
1999
- };
2000
-
2001
- /**
2002
- * Return actions needed for the specified state of this annotation.
2003
- *
2004
- * @param {string} [state] The state to return actions for. Defaults to
2005
- * the current state.
2006
- * @returns {geo.actionRecord[]} A list of actions.
2007
- */
2008
- this.actions = function (state) {
2009
- return continuousVerticesActions(m_this, s_actions, state, 'polygon', arguments);
2010
- };
2011
-
2012
- /**
2013
- * Process any actions for this annotation.
2014
- *
2015
- * @param {geo.event} evt The action event.
2016
- * @returns {boolean|string} `true` to update the annotation, `'done'` if the
2017
- * annotation was completed (changed from create to done state),
2018
- * `'remove'` if the annotation should be removed, falsy to not update
2019
- * anything.
2020
- */
2021
- this.processAction = function (evt) {
2022
- return continuousVerticesProcessAction(m_this, evt, 'polygon');
2023
- };
2024
-
2025
- /**
2026
- * Return the coordinates to be stored in a geojson geometry object.
2027
- *
2028
- * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
2029
- * gcs, `null` to use the map gcs, or any other transform.
2030
- * @returns {array} An array of flattened coordinates in the interface gcs
2031
- * coordinate system. `undefined` if this annotation is incomplete.
2032
- */
2033
- this._geojsonCoordinates = function (gcs) {
2034
- var src = m_this.coordinates(gcs);
2035
- if (!src || (!src.outer && src.length < 3) || m_this.state() === annotationState.create) {
2036
- return;
2037
- }
2038
- var coord = [];
2039
- if (!src.outer) {
2040
- coord = [src.map((pt) => [pt.x, pt.y])];
2041
- coord[0].push(coord[0][0].slice());
2042
- } else {
2043
- coord = [src.outer.map((pt) => [pt.x, pt.y])];
2044
- coord[0].push(coord[0][0].slice());
2045
- (src.inner || []).forEach((h) => {
2046
- const poly = h.map((pt) => [pt.x, pt.y]);
2047
- poly.push(poly[0].slice());
2048
- coord.push(poly);
2049
- });
2050
- }
2051
- return coord;
2052
- };
2053
-
2054
- /**
2055
- * Return the geometry type that is used to store this annotation in geojson.
2056
- *
2057
- * @returns {string} A geojson geometry type.
2058
- */
2059
- this._geojsonGeometryType = function () {
2060
- return 'Polygon';
2061
- };
2062
-
2063
- /**
2064
- * Return a list of styles that should be preserved in a geojson
2065
- * representation of the annotation.
2066
- *
2067
- * @returns {string[]} A list of style names to store.
2068
- */
2069
- this._geojsonStyles = function () {
2070
- return [
2071
- 'fill', 'fillColor', 'fillOpacity', 'lineCap', 'lineJoin', 'stroke',
2072
- 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth'];
2073
- };
2074
- };
2075
- inherit(polygonAnnotation, annotation);
2076
-
2077
- var polygonRequiredFeatures = {};
2078
- polygonRequiredFeatures[polygonFeature.capabilities.feature] = true;
2079
- polygonRequiredFeatures[lineFeature.capabilities.basic] = [annotationState.create];
2080
- registerAnnotation('polygon', polygonAnnotation, polygonRequiredFeatures);
2081
-
2082
- /**
2083
- * Line annotation specification. Extends {@link geo.annotation.spec}.
2084
- *
2085
- * @typedef {object} geo.lineAnnotation.spec
2086
- * @extends geo.annotation.spec
2087
- * @property {geo.geoPosition[]} [vertices] A list of vertices in map gcs
2088
- * coordinates.
2089
- * @property {geo.geoPosition[]} [coordinates] An alternate name for
2090
- * `vertices`.
2091
- * @property {geo.lineFeature.styleSpec} [style] The style to apply to a
2092
- * finished line. This uses styles for {@link geo.lineFeature}.
2093
- * @property {geo.lineFeature.styleSpec} [editStyle] The style to apply to a
2094
- * line in edit mode.
2095
- */
2096
-
2097
- /**
2098
- * Line annotation class.
2099
- *
2100
- * @class
2101
- * @alias geo.lineAnnotation
2102
- * @extends geo.annotation
2103
- *
2104
- * @param {geo.lineAnnotation.spec?} [args] Options for the annotation.
2105
- */
2106
- var lineAnnotation = function (args) {
2107
- 'use strict';
2108
- if (!(this instanceof lineAnnotation)) {
2109
- return new lineAnnotation(args);
2110
- }
2111
-
2112
- args = $.extend(true, {}, {
2113
- style: {
2114
- line: function (d) {
2115
- /* Return an array that has the same number of items as we have
2116
- * vertices. */
2117
- return Array.apply(null, Array(m_this.options('vertices').length)).map(
2118
- function () { return d; });
2119
- },
2120
- position: function (d, i) {
2121
- return m_this.options('vertices')[i];
2122
- },
2123
- strokeColor: {r: 0, g: 0, b: 0},
2124
- strokeOpacity: 1,
2125
- strokeWidth: 3,
2126
- closed: false,
2127
- lineCap: 'butt',
2128
- lineJoin: 'miter'
2129
- },
2130
- highlightStyle: {
2131
- strokeWidth: 5
2132
- },
2133
- createStyle: {
2134
- line: function (d) {
2135
- /* Return an array that has the same number of items as we have
2136
- * vertices. */
2137
- return Array.apply(null, Array(m_this.options('vertices').length)).map(
2138
- function () { return d; });
2139
- },
2140
- position: function (d, i) {
2141
- return m_this.options('vertices')[i];
2142
- },
2143
- strokeColor: {r: 0, g: 0, b: 1},
2144
- strokeOpacity: 1,
2145
- strokeWidth: 3,
2146
- closed: false,
2147
- lineCap: 'butt',
2148
- lineJoin: 'miter'
2149
- }
2150
- }, args || {});
2151
- args.vertices = args.vertices || args.coordinates || [];
2152
- delete args.coordinates;
2153
- annotation.call(this, 'line', args);
2154
-
2155
- var m_this = this,
2156
- s_actions = this.actions,
2157
- s_processEditAction = this.processEditAction;
2158
-
2159
- /**
2160
- * Get a list of renderable features for this annotation.
2161
- *
2162
- * @returns {array} An array of features.
2163
- */
2164
- this.features = function () {
2165
- var opt = m_this.options(),
2166
- state = m_this.state(),
2167
- features;
2168
- switch (state) {
2169
- case annotationState.create:
2170
- features = [{
2171
- line: {
2172
- line: opt.vertices,
2173
- style: m_this.styleForState(state)
2174
- }
2175
- }];
2176
- break;
2177
- default:
2178
- features = [{
2179
- line: {
2180
- line: opt.vertices,
2181
- style: m_this.styleForState(state)
2182
- }
2183
- }];
2184
- if (state === annotationState.edit) {
2185
- m_this._addEditHandles(features, opt.vertices, undefined, !m_this.style('closed'));
2186
- }
2187
- break;
2188
- }
2189
- return features;
2190
- };
2191
-
2192
- /**
2193
- * Get and optionally set coordinates associated with this annotation in the
2194
- * map gcs coordinate system.
2195
- *
2196
- * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
2197
- * to set.
2198
- * @returns {geo.geoPosition[]} The current array of coordinates.
2199
- */
2200
- this._coordinates = function (coordinates) {
2201
- if (coordinates) {
2202
- m_this.options('vertices', coordinates);
2203
- }
2204
- return m_this.options('vertices');
2205
- };
2206
-
2207
- /**
2208
- * Handle a mouse move on this annotation.
2209
- *
2210
- * @param {geo.event} evt The mouse move event.
2211
- * @returns {boolean} Truthy to update the annotation, falsy to not
2212
- * update anything.
2213
- */
2214
- this.mouseMove = function (evt) {
2215
- if (m_this.state() !== annotationState.create) {
2216
- return;
2217
- }
2218
- var vertices = m_this.options('vertices');
2219
- if (vertices.length) {
2220
- vertices[vertices.length - 1] = evt.mapgcs;
2221
- return true;
2222
- }
2223
- };
2224
-
2225
- /**
2226
- * Handle a mouse click on this annotation. If the event is processed,
2227
- * evt.handled should be set to `true` to prevent further processing.
2228
- *
2229
- * @param {geo.event} evt The mouse click event.
2230
- * @returns {boolean|string} `true` to update the annotation, `'done'` if
2231
- * the annotation was completed (changed from create to done state),
2232
- * `'remove'` if the annotation should be removed, falsy to not update
2233
- * anything.
2234
- */
2235
- this.mouseClick = function (evt) {
2236
- var layer = m_this.layer();
2237
- if (m_this.state() !== annotationState.create || !layer) {
2238
- return;
2239
- }
2240
- var end = !!evt.buttonsDown.right, skip;
2241
- if (!evt.buttonsDown.left && !evt.buttonsDown.right) {
2242
- return;
2243
- }
2244
- var vertices = m_this.options('vertices');
2245
- if (evt.buttonsDown.right && !vertices.length) {
2246
- return;
2247
- }
2248
- evt.handled = true;
2249
- if (evt.buttonsDown.left) {
2250
- if (vertices.length) {
2251
- if (vertices.length >= 2 && layer.displayDistance(
2252
- vertices[vertices.length - 2], null, evt.map, 'display') <=
2253
- layer.options('adjacentPointProximity')) {
2254
- skip = true;
2255
- if (m_this._lastClick &&
2256
- evt.time - m_this._lastClick < layer.options('dblClickTime')) {
2257
- end = true;
2258
- }
2259
- } else if (vertices.length >= 2 && layer.displayDistance(
2260
- vertices[0], null, evt.map, 'display') <=
2261
- layer.options('finalPointProximity')) {
2262
- end = 'close';
2263
- } else {
2264
- vertices[vertices.length - 1] = evt.mapgcs;
2265
- }
2266
- } else {
2267
- vertices.push(evt.mapgcs);
2268
- }
2269
- if (!end && !skip) {
2270
- vertices.push(evt.mapgcs);
2271
- }
2272
- m_this._lastClick = evt.time;
2273
- m_this._lastClickVertexCount = vertices.length;
2274
- }
2275
- if (end) {
2276
- if (vertices.length < 3) {
2277
- return 'remove';
2278
- }
2279
- vertices.pop();
2280
- m_this.options('style').closed = end === 'close';
2281
- m_this.state(annotationState.done);
2282
- return 'done';
2283
- }
2284
- return !skip;
2285
- };
2286
-
2287
- /**
2288
- * Return actions needed for the specified state of this annotation.
2289
- *
2290
- * @param {string} [state] The state to return actions for. Defaults to
2291
- * the current state.
2292
- * @returns {geo.actionRecord[]} A list of actions.
2293
- */
2294
- this.actions = function (state) {
2295
- return continuousVerticesActions(m_this, s_actions, state, 'line', arguments);
2296
- };
2297
-
2298
- /**
2299
- * Process any actions for this annotation.
2300
- *
2301
- * @param {geo.event} evt The action event.
2302
- * @returns {boolean|string} `true` to update the annotation, `'done'` if the
2303
- * annotation was completed (changed from create to done state),
2304
- * `'remove'` if the annotation should be removed, falsy to not update
2305
- * anything.
2306
- */
2307
- this.processAction = function (evt) {
2308
- return continuousVerticesProcessAction(m_this, evt, 'line');
2309
- };
2310
-
2311
- /**
2312
- * Return the coordinates to be stored in a geojson geometry object.
2313
- *
2314
- * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
2315
- * gcs, `null` to use the map gcs, or any other transform.
2316
- * @returns {array} An array of flattened coordinates in the interface gcs
2317
- * coordinate system. `undefined` if this annotation is incomplete.
2318
- */
2319
- this._geojsonCoordinates = function (gcs) {
2320
- var src = m_this.coordinates(gcs);
2321
- if (!src || src.length < 2 || m_this.state() === annotationState.create) {
2322
- return;
2323
- }
2324
- var coord = [];
2325
- for (var i = 0; i < src.length; i += 1) {
2326
- coord.push([src[i].x, src[i].y]);
2327
- }
2328
- return coord;
2329
- };
2330
-
2331
- /**
2332
- * Return the geometry type that is used to store this annotation in geojson.
2333
- *
2334
- * @returns {string} A geojson geometry type.
2335
- */
2336
- this._geojsonGeometryType = function () {
2337
- return 'LineString';
2338
- };
2339
-
2340
- /**
2341
- * Return a list of styles that should be preserved in a geojson
2342
- * representation of the annotation.
2343
- *
2344
- * @returns {string[]} A list of style names to store.
2345
- */
2346
- this._geojsonStyles = function () {
2347
- return [
2348
- 'closed', 'lineCap', 'lineJoin', 'strokeColor', 'strokeOffset',
2349
- 'strokeOpacity', 'strokeWidth'];
2350
- };
2351
-
2352
- /**
2353
- * Process any edit actions for this annotation.
2354
- *
2355
- * @param {geo.event} evt The action event.
2356
- * @returns {boolean|string} `true` to update the annotation, falsy to not
2357
- * update anything.
2358
- */
2359
- this.processEditAction = function (evt) {
2360
- switch (m_this._editHandle.handle.type) {
2361
- case 'vertex':
2362
- return m_this._processEditActionVertex(evt, true);
2363
- }
2364
- return s_processEditAction.apply(m_this, arguments);
2365
- };
2366
-
2367
- /**
2368
- * Handle a mouse click on this annotation when in edit mode. If the event
2369
- * is processed, evt.handled should be set to `true` to prevent further
2370
- * processing.
2371
- *
2372
- * @param {geo.event} evt The mouse click event.
2373
- * @returns {boolean|string} `true` to update the annotation, `'done'` if
2374
- * the annotation was completed (changed from create to done state),
2375
- * `'remove'` if the annotation should be removed, falsy to not update
2376
- * anything.
2377
- */
2378
- this.mouseClickEdit = function (evt) {
2379
- // if we get a left double click on an edge on a closed line, break the
2380
- // line at that edge
2381
- var layer = m_this.layer(),
2382
- handle = m_this._editHandle,
2383
- split;
2384
- // ensure we are in edit mode and this is a left click
2385
- if (m_this.state() !== annotationState.edit || !layer || !evt.buttonsDown.left) {
2386
- return;
2387
- }
2388
- // ensure this is an edge on a closed line
2389
- if (!handle || !handle.handle.selected || handle.handle.type !== 'edge' || !m_this.options('style').closed) {
2390
- return;
2391
- }
2392
- evt.handled = true;
2393
- if (m_this._lastClick && evt.time - m_this._lastClick < layer.options('dblClickTime')) {
2394
- split = true;
2395
- }
2396
- m_this._lastClick = evt.time;
2397
- if (split) {
2398
- var index = handle.handle.index,
2399
- curPts = m_this._coordinates(),
2400
- pts = curPts.slice(index + 1).concat(curPts.slice(0, index + 1));
2401
- m_this._coordinates(pts);
2402
- m_this.options('style').closed = false;
2403
- handle.handle.index = undefined;
2404
- return true;
2405
- }
2406
- };
2407
-
2408
- };
2409
- inherit(lineAnnotation, annotation);
2410
-
2411
- var lineRequiredFeatures = {};
2412
- lineRequiredFeatures[lineFeature.capabilities.basic] = [annotationState.create];
2413
- registerAnnotation('line', lineAnnotation, lineRequiredFeatures);
2414
-
2415
- /**
2416
- * Point annotation specification. Extends {@link geo.annotation.spec}.
2417
- *
2418
- * @typedef {object} geo.pointAnnotation.spec
2419
- * @extends geo.annotation.spec
2420
- * @property {geo.geoPosition} [position] A coordinate in map gcs coordinates.
2421
- * @property {geo.geoPosition[]} [coordinates] An array with one coordinate to
2422
- * use in place of `position`.
2423
- * @property {geo.pointFeature.styleSpec} [style] The style to apply to a
2424
- * finished point. This uses styles for {@link geo.pointFeature}.
2425
- * @property {boolean|number} [style.scaled=false] If `false`, the point is not
2426
- * scaled with zoom level. If `true`, the radius is based on the zoom level
2427
- * at first instantiation. If a number, the radius is used at the `scaled`
2428
- * zoom level.
2429
- * @property {geo.pointFeature.styleSpec} [editStyle] The style to apply to a
2430
- * point in edit mode.
2431
- */
2432
-
2433
- /**
2434
- * Point annotation class.
2435
- *
2436
- * @class
2437
- * @alias geo.pointAnnotation
2438
- * @extends geo.annotation
2439
- *
2440
- * @param {geo.pointAnnotation.spec?} [args] Options for the annotation.
2441
- */
2442
- var pointAnnotation = function (args) {
2443
- 'use strict';
2444
- if (!(this instanceof pointAnnotation)) {
2445
- return new pointAnnotation(args);
2446
- }
2447
-
2448
- args = $.extend(true, {}, {
2449
- style: {
2450
- fill: true,
2451
- fillColor: {r: 0, g: 1, b: 0},
2452
- fillOpacity: 0.25,
2453
- radius: 10,
2454
- scaled: false,
2455
- stroke: true,
2456
- strokeColor: {r: 0, g: 0, b: 0},
2457
- strokeOpacity: 1,
2458
- strokeWidth: 3
2459
- },
2460
- createStyle: {
2461
- fillColor: {r: 0.3, g: 0.3, b: 0.3},
2462
- fillOpacity: 0.25,
2463
- strokeColor: {r: 0, g: 0, b: 1}
2464
- },
2465
- highlightStyle: {
2466
- fillColor: {r: 0, g: 1, b: 1},
2467
- fillOpacity: 0.5,
2468
- strokeWidth: 5
2469
- }
2470
- }, args || {});
2471
- args.position = args.position || (args.coordinates ? args.coordinates[0] : undefined);
2472
- delete args.coordinates;
2473
- annotation.call(this, 'point', args);
2474
-
2475
- var m_this = this;
2476
-
2477
- /**
2478
- * Get a list of renderable features for this annotation.
2479
- *
2480
- * @returns {array} An array of features.
2481
- */
2482
- this.features = function () {
2483
- var opt = m_this.options(),
2484
- state = m_this.state(),
2485
- features, style, scaleOnZoom;
2486
- switch (state) {
2487
- case annotationState.create:
2488
- features = [];
2489
- break;
2490
- default:
2491
- style = m_this.styleForState(state);
2492
- if (opt.style.scaled || opt.style.scaled === 0) {
2493
- if (opt.style.scaled === true) {
2494
- opt.style.scaled = m_this.layer().map().zoom();
2495
- }
2496
- style = $.extend({}, style, {
2497
- radius: function () {
2498
- var radius = opt.style.radius,
2499
- zoom = m_this.layer().map().zoom();
2500
- if (util.isFunction(radius)) {
2501
- radius = radius.apply(m_this, arguments);
2502
- }
2503
- radius *= Math.pow(2, zoom - opt.style.scaled);
2504
- return radius;
2505
- }
2506
- });
2507
- scaleOnZoom = true;
2508
- }
2509
- features = [{
2510
- point: {
2511
- x: opt.position.x,
2512
- y: opt.position.y,
2513
- style: style,
2514
- scaleOnZoom: scaleOnZoom
2515
- }
2516
- }];
2517
- if (state === annotationState.edit) {
2518
- m_this._addEditHandles(
2519
- features, [opt.position],
2520
- {edge: false, center: false, resize: false, rotate: false});
2521
- }
2522
- break;
2523
- }
2524
- return features;
2525
- };
2526
-
2527
- /**
2528
- * Get and optionally set coordinates associated with this annotation in the
2529
- * map gcs coordinate system.
2530
- *
2531
- * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
2532
- * to set.
2533
- * @returns {geo.geoPosition[]} The current array of coordinates.
2534
- */
2535
- this._coordinates = function (coordinates) {
2536
- if (coordinates && coordinates.length >= 1) {
2537
- m_this.options('position', coordinates[0]);
2538
- }
2539
- if (m_this.state() === annotationState.create) {
2540
- return [];
2541
- }
2542
- return [m_this.options('position')];
2543
- };
2544
-
2545
- this._coordinateOption = 'position';
2546
-
2547
- /**
2548
- * Handle a mouse click on this annotation. If the event is processed,
2549
- * evt.handled should be set to `true` to prevent further processing.
2550
- *
2551
- * @param {geo.event} evt The mouse click event.
2552
- * @returns {boolean|string} `true` to update the annotation, `'done'` if
2553
- * the annotation was completed (changed from create to done state),
2554
- * `'remove'` if the annotation should be removed, falsy to not update
2555
- * anything.
2556
- */
2557
- this.mouseClick = function (evt) {
2558
- if (m_this.state() !== annotationState.create) {
2559
- return;
2560
- }
2561
- if (!evt.buttonsDown.left) {
2562
- return;
2563
- }
2564
- evt.handled = true;
2565
- m_this.options('position', evt.mapgcs);
2566
- m_this.state(annotationState.done);
2567
- return 'done';
2568
- };
2569
-
2570
- /**
2571
- * Return a list of styles that should be preserved in a geojson
2572
- * representation of the annotation.
2573
- *
2574
- * @returns {string[]} A list of style names to store.
2575
- */
2576
- this._geojsonStyles = function () {
2577
- return [
2578
- 'fill', 'fillColor', 'fillOpacity', 'radius', 'scaled', 'stroke',
2579
- 'strokeColor', 'strokeOpacity', 'strokeWidth'];
2580
- };
2581
-
2582
- /**
2583
- * Return the coordinates to be stored in a geojson geometry object.
2584
- *
2585
- * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
2586
- * gcs, `null` to use the map gcs, or any other transform.
2587
- * @returns {array} An array of flattened coordinates in the interface gcs
2588
- * coordinate system. `undefined` if this annotation is incomplete.
2589
- */
2590
- this._geojsonCoordinates = function (gcs) {
2591
- var src = m_this.coordinates(gcs);
2592
- if (!src || m_this.state() === annotationState.create || src.length < 1 || src[0] === undefined) {
2593
- return;
2594
- }
2595
- return [src[0].x, src[0].y];
2596
- };
2597
-
2598
- /**
2599
- * Return the geometry type that is used to store this annotation in geojson.
2600
- *
2601
- * @returns {string} A geojson geometry type.
2602
- */
2603
- this._geojsonGeometryType = function () {
2604
- return 'Point';
2605
- };
2606
- };
2607
- inherit(pointAnnotation, annotation);
2608
-
2609
- var pointRequiredFeatures = {};
2610
- pointRequiredFeatures[pointFeature.capabilities.feature] = true;
2611
- registerAnnotation('point', pointAnnotation, pointRequiredFeatures);
2612
-
2613
1264
  /**
2614
1265
  * Return a function that can be used as a selectionConstraint that requires
2615
1266
  * that the aspect ratio of a rectangle-like selection is a specific value or
2616
1267
  * range of values.
2617
1268
  *
2618
- * @param {number|number[]} ratio Either a single aspect ratio or a list of
2619
- * allowed aspect ratios. For instance, 1 will require that the selection
2620
- * square, 2 would require that it is twice as wide as tall, [2, 1/2] would
2621
- * allow it to be twice as wide or half as wide as it is tall.
1269
+ * @param {number|number[]|geo.geoSize|geo.geoSize[]} ratio Either a single
1270
+ * aspect ratio, a single size, or a list of allowed aspect ratios and sizes.
1271
+ * For instance, 1 will require that the selection square, 2 would require
1272
+ * that it is twice as wide as tall, [2, 1/2] would allow it to be twice as
1273
+ * wide or half as wide as it is tall. Sizes (e.g., {width: 400, height:
1274
+ * 500}) snap to that size.
2622
1275
  * @returns {function} A function that can be passed to the mapIterator
2623
1276
  * selectionConstraint or to an annotation constraint function.
2624
1277
  */
@@ -2664,16 +1317,23 @@ function constrainAspectRatio(ratio) {
2664
1317
  const area = Math.abs(dist1 * dist3);
2665
1318
  let shape, edge;
2666
1319
  ratios.forEach((ratio) => {
2667
- if (ratio !== 1 && !(index % 2)) {
2668
- ratio = 1.0 / ratio;
1320
+ let width, height;
1321
+ if (ratio.width) {
1322
+ width = ratio.width;
1323
+ height = ratio.height;
1324
+ } else {
1325
+ width = (area * ratio) ** 0.5;
1326
+ height = width / ratio;
1327
+ }
1328
+ if (width !== height && !(index % 2)) {
1329
+ [width, height] = [height, width];
2669
1330
  }
2670
- const width = (area * ratio) ** 0.5;
2671
- const score = (width - dist3) ** 2 + (width / ratio - dist1) ** 2;
1331
+ const score = (width - dist3) ** 2 + (height - dist1) ** 2;
2672
1332
  if (best === undefined || score < best) {
2673
1333
  best = score;
2674
1334
  shape = {
2675
1335
  w: width,
2676
- h: width / ratio
1336
+ h: height
2677
1337
  };
2678
1338
  }
2679
1339
  });
@@ -2715,10 +1375,17 @@ function constrainAspectRatio(ratio) {
2715
1375
  /* Not in edit vertex or edge mode */
2716
1376
  const area = Math.abs((pos.x - origin.x) * (pos.y - origin.y));
2717
1377
  ratios.forEach((ratio) => {
2718
- const width = (area * ratio) ** 0.5;
1378
+ let width, height;
1379
+ if (ratio.width) {
1380
+ width = ratio.width;
1381
+ height = ratio.height;
1382
+ } else {
1383
+ width = (area * ratio) ** 0.5;
1384
+ height = width / ratio;
1385
+ }
2719
1386
  const adjusted = {
2720
1387
  x: origin.x + Math.sign(pos.x - origin.x) * width,
2721
- y: origin.y + Math.sign(pos.y - origin.y) * width / ratio
1388
+ y: origin.y + Math.sign(pos.y - origin.y) * height
2722
1389
  };
2723
1390
  const score = (adjusted.x - pos.x) ** 2 + (adjusted.y - pos.y) ** 2;
2724
1391
  if (best === undefined || score < best) {
@@ -2745,14 +1412,11 @@ module.exports = {
2745
1412
  state: annotationState,
2746
1413
  actionOwner: annotationActionOwner,
2747
1414
  annotation: annotation,
2748
- lineAnnotation: lineAnnotation,
2749
- pointAnnotation: pointAnnotation,
2750
- polygonAnnotation: polygonAnnotation,
2751
- rectangleAnnotation: rectangleAnnotation,
2752
- squareAnnotation: squareAnnotation,
2753
- ellipseAnnotation: ellipseAnnotation,
2754
- circleAnnotation: circleAnnotation,
2755
1415
  _editHandleFeatureLevel: editHandleFeatureLevel,
2756
- defaultEditHandleStyle: defaultEditHandleStyle,
2757
- constrainAspectRatio: constrainAspectRatio
1416
+ defaultEditHandleStyle,
1417
+ constrainAspectRatio,
1418
+ // these aren't exposed in index.js
1419
+ annotationActionOwner,
1420
+ continuousVerticesActions,
1421
+ continuousVerticesProcessAction
2758
1422
  };