mapshaper 0.6.69 → 0.6.71
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/mapshaper.js +94 -17
- package/package.json +1 -1
- package/www/index.html +1 -0
- package/www/mapshaper-gui.js +1240 -659
- package/www/mapshaper.js +94 -17
- package/www/page.css +13 -9
package/www/mapshaper-gui.js
CHANGED
|
@@ -2532,48 +2532,12 @@
|
|
|
2532
2532
|
};
|
|
2533
2533
|
}
|
|
2534
2534
|
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
if (isProjectedLayer(lyr)) {
|
|
2538
|
-
lyr.arcs.flatten();
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
function setZ(lyr, z) {
|
|
2543
|
-
lyr.source.dataset.arcs.setRetainedInterval(z);
|
|
2544
|
-
if (isProjectedLayer(lyr)) {
|
|
2545
|
-
lyr.arcs.setRetainedInterval(z);
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
|
|
2549
|
-
function updateZ(lyr) {
|
|
2550
|
-
if (isProjectedLayer(lyr) && !lyr.source.dataset.arcs.isFlat()) {
|
|
2551
|
-
lyr.arcs.setThresholds(lyr.source.dataset.arcs.getVertexData().zz);
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
|
|
2555
|
-
function insertVertex$1(lyr, id, dataPoint) {
|
|
2556
|
-
internal.insertVertex(lyr.source.dataset.arcs, id, dataPoint);
|
|
2557
|
-
if (isProjectedLayer(lyr)) {
|
|
2558
|
-
internal.insertVertex(lyr.arcs, id, lyr.projectPoint(dataPoint[0], dataPoint[1]));
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
function deleteVertex$1(lyr, id) {
|
|
2563
|
-
internal.deleteVertex(lyr.arcs, id);
|
|
2564
|
-
if (isProjectedLayer(lyr)) {
|
|
2565
|
-
internal.deleteVertex(lyr.source.dataset.arcs, id);
|
|
2566
|
-
}
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2535
|
+
// Convert a point from display CRS coordinates to data coordinates.
|
|
2536
|
+
// These are only different when using dynamic reprojection (basemap view).
|
|
2569
2537
|
function translateDisplayPoint(lyr, p) {
|
|
2570
2538
|
return isProjectedLayer(lyr) ? lyr.invertPoint(p[0], p[1]) : p;
|
|
2571
2539
|
}
|
|
2572
2540
|
|
|
2573
|
-
function getPointCoords(lyr, fid) {
|
|
2574
|
-
return internal.cloneShape(lyr.source.layer.shapes[fid]);
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
2541
|
// bbox: display coords
|
|
2578
2542
|
// intended to work with rectangular projections like Mercator
|
|
2579
2543
|
function getBBoxCoords(lyr, bbox) {
|
|
@@ -2586,58 +2550,519 @@
|
|
|
2586
2550
|
return bounds.toArray();
|
|
2587
2551
|
}
|
|
2588
2552
|
|
|
2589
|
-
function
|
|
2590
|
-
|
|
2553
|
+
function isProjectedLayer(lyr) {
|
|
2554
|
+
// TODO: could do some validation on the layer's contents
|
|
2555
|
+
return !!(lyr.source && lyr.invertPoint);
|
|
2591
2556
|
}
|
|
2592
2557
|
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2558
|
+
var darkStroke = "#334",
|
|
2559
|
+
lightStroke = "#b7d9ea",
|
|
2560
|
+
violet = "#cc6acc",
|
|
2561
|
+
violetFill = "rgba(249, 120, 249, 0.20)",
|
|
2562
|
+
gold = "#efc100",
|
|
2563
|
+
black = "black",
|
|
2564
|
+
grey = "#888",
|
|
2565
|
+
selectionFill = "rgba(237, 214, 0, 0.12)",
|
|
2566
|
+
hoverFill = "rgba(255, 120, 255, 0.12)",
|
|
2567
|
+
activeStyle = { // outline style for the active layer
|
|
2568
|
+
type: 'outline',
|
|
2569
|
+
strokeColors: [lightStroke, darkStroke],
|
|
2570
|
+
strokeWidth: 0.8,
|
|
2571
|
+
dotColor: "#223",
|
|
2572
|
+
dotSize: 1
|
|
2573
|
+
},
|
|
2574
|
+
activeStyleDarkMode = {
|
|
2575
|
+
type: 'outline',
|
|
2576
|
+
strokeColors: [lightStroke, 'white'],
|
|
2577
|
+
strokeWidth: 0.9,
|
|
2578
|
+
dotColor: 'white',
|
|
2579
|
+
dotSize: 1
|
|
2580
|
+
},
|
|
2581
|
+
activeStyleForLabels = {
|
|
2582
|
+
dotColor: "rgba(250, 0, 250, 0.45)", // violet dot with transparency
|
|
2583
|
+
dotSize: 1
|
|
2584
|
+
},
|
|
2585
|
+
referenceStyle = { // outline style for reference layers
|
|
2586
|
+
type: 'outline',
|
|
2587
|
+
strokeColors: [null, '#78c110'], // upped saturation from #86c927
|
|
2588
|
+
strokeWidth: 0.85,
|
|
2589
|
+
dotColor: "#73ba20",
|
|
2590
|
+
dotSize: 1
|
|
2591
|
+
},
|
|
2592
|
+
intersectionStyle = {
|
|
2593
|
+
dotColor: "#F24400",
|
|
2594
|
+
dotSize: 1
|
|
2595
|
+
},
|
|
2596
|
+
hoverStyles = {
|
|
2597
|
+
polygon: {
|
|
2598
|
+
fillColor: hoverFill,
|
|
2599
|
+
strokeColor: black,
|
|
2600
|
+
strokeWidth: 1.2
|
|
2601
|
+
}, point: {
|
|
2602
|
+
dotColor: violet, // black,
|
|
2603
|
+
dotSize: 2.5
|
|
2604
|
+
}, polyline: {
|
|
2605
|
+
strokeColor: black,
|
|
2606
|
+
strokeWidth: 2.5,
|
|
2607
|
+
}
|
|
2608
|
+
},
|
|
2609
|
+
unselectedHoverStyles = {
|
|
2610
|
+
polygon: {
|
|
2611
|
+
fillColor: 'rgba(0,0,0,0)',
|
|
2612
|
+
strokeColor: black,
|
|
2613
|
+
strokeWidth: 1.2
|
|
2614
|
+
}, point: {
|
|
2615
|
+
dotColor: black, // grey,
|
|
2616
|
+
dotSize: 2
|
|
2617
|
+
}, polyline: {
|
|
2618
|
+
strokeColor: black, // grey,
|
|
2619
|
+
strokeWidth: 2.5
|
|
2620
|
+
}
|
|
2621
|
+
},
|
|
2622
|
+
selectionStyles = {
|
|
2623
|
+
polygon: {
|
|
2624
|
+
fillColor: hoverFill,
|
|
2625
|
+
strokeColor: black,
|
|
2626
|
+
strokeWidth: 1.2
|
|
2627
|
+
}, point: {
|
|
2628
|
+
dotColor: violet, // black,
|
|
2629
|
+
dotSize: 1.5
|
|
2630
|
+
}, polyline: {
|
|
2631
|
+
strokeColor: violet, // black,
|
|
2632
|
+
strokeWidth: 2.5
|
|
2633
|
+
}
|
|
2634
|
+
},
|
|
2635
|
+
// not used
|
|
2636
|
+
selectionHoverStyles = {
|
|
2637
|
+
polygon: {
|
|
2638
|
+
fillColor: selectionFill,
|
|
2639
|
+
strokeColor: black,
|
|
2640
|
+
strokeWidth: 1.2
|
|
2641
|
+
}, point: {
|
|
2642
|
+
dotColor: black,
|
|
2643
|
+
dotSize: 1.5
|
|
2644
|
+
}, polyline: {
|
|
2645
|
+
strokeColor: black,
|
|
2646
|
+
strokeWidth: 2
|
|
2647
|
+
}
|
|
2648
|
+
},
|
|
2649
|
+
pinnedStyles = {
|
|
2650
|
+
polygon: {
|
|
2651
|
+
fillColor: violetFill,
|
|
2652
|
+
strokeColor: violet,
|
|
2653
|
+
strokeWidth: 1.8
|
|
2654
|
+
}, point: {
|
|
2655
|
+
dotColor: violet,
|
|
2656
|
+
dotSize: 3
|
|
2657
|
+
}, polyline: {
|
|
2658
|
+
strokeColor: violet, // black, // violet,
|
|
2659
|
+
strokeWidth: 3
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
|
|
2663
|
+
function getIntersectionStyle(lyr, opts) {
|
|
2664
|
+
return getDefaultStyle(lyr, intersectionStyle);
|
|
2599
2665
|
}
|
|
2600
2666
|
|
|
2601
|
-
//
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2667
|
+
// Style for unselected layers with visibility turned on
|
|
2668
|
+
// (styled layers have)
|
|
2669
|
+
function getReferenceLayerStyle(lyr, opts) {
|
|
2670
|
+
var style;
|
|
2671
|
+
if (layerHasCanvasDisplayStyle(lyr) && !opts.outlineMode) {
|
|
2672
|
+
style = getCanvasDisplayStyle(lyr);
|
|
2673
|
+
} else if (internal.layerHasLabels(lyr) && !opts.outlineMode) {
|
|
2674
|
+
style = {dotSize: 0}; // no reference dots if labels are visible
|
|
2675
|
+
} else {
|
|
2676
|
+
style = getDefaultStyle(lyr, referenceStyle);
|
|
2606
2677
|
}
|
|
2678
|
+
return style;
|
|
2607
2679
|
}
|
|
2608
2680
|
|
|
2609
|
-
function
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2681
|
+
function getActiveLayerStyle(lyr, opts) {
|
|
2682
|
+
var style;
|
|
2683
|
+
if (layerHasCanvasDisplayStyle(lyr) && !opts.outlineMode) {
|
|
2684
|
+
style = getCanvasDisplayStyle(lyr);
|
|
2685
|
+
} else if (internal.layerHasLabels(lyr) && !opts.outlineMode) {
|
|
2686
|
+
style = getDefaultStyle(lyr, activeStyleForLabels);
|
|
2687
|
+
} else if (opts.darkMode) {
|
|
2688
|
+
style = getDefaultStyle(lyr, activeStyleDarkMode);
|
|
2689
|
+
} else {
|
|
2690
|
+
style = getDefaultStyle(lyr, activeStyle);
|
|
2691
|
+
}
|
|
2692
|
+
return style;
|
|
2613
2693
|
}
|
|
2614
2694
|
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2695
|
+
// Returns a display style for the overlay layer.
|
|
2696
|
+
// The overlay layer renders several kinds of feature, each of which is displayed
|
|
2697
|
+
// with a different style.
|
|
2698
|
+
//
|
|
2699
|
+
// * hover shapes
|
|
2700
|
+
// * selected shapes
|
|
2701
|
+
// * pinned shapes
|
|
2702
|
+
//
|
|
2703
|
+
function getOverlayStyle(baseLyr, o, opts) {
|
|
2704
|
+
if (opts.interactionMode == 'vertices') {
|
|
2705
|
+
return getVertexStyle(baseLyr, o);
|
|
2706
|
+
}
|
|
2707
|
+
if (opts.interactionMode == 'edit-lines') {
|
|
2708
|
+
return getLineEditingStyle(baseLyr, o);
|
|
2709
|
+
}
|
|
2710
|
+
var geomType = baseLyr.geometry_type;
|
|
2711
|
+
var topId = o.id; // pinned id (if pinned) or hover id
|
|
2712
|
+
var topIdx = -1;
|
|
2713
|
+
var styler = function(style, i) {
|
|
2714
|
+
var defaultStyle = i === topIdx ? topStyle : outlineStyle;
|
|
2715
|
+
if (baseStyle.styler) {
|
|
2716
|
+
// TODO: render default stroke widths without scaling
|
|
2717
|
+
// (will need to pass symbol scale to the styler function)
|
|
2718
|
+
style.strokeWidth = defaultStyle.strokeWidth;
|
|
2719
|
+
baseStyle.styler(style, i); // get styled stroke width (if set)
|
|
2720
|
+
style.strokeColor = defaultStyle.strokeColor;
|
|
2721
|
+
style.fillColor = defaultStyle.fillColor;
|
|
2722
|
+
} else {
|
|
2723
|
+
Object.assign(style, defaultStyle);
|
|
2621
2724
|
}
|
|
2725
|
+
};
|
|
2726
|
+
var baseStyle = getActiveLayerStyle(baseLyr, opts);
|
|
2727
|
+
var outlineStyle = getDefaultStyle(baseLyr, selectionStyles[geomType]);
|
|
2728
|
+
var topStyle;
|
|
2729
|
+
var ids = o.ids.filter(function(i) {
|
|
2730
|
+
return i != o.id; // move selected id to the end
|
|
2622
2731
|
});
|
|
2732
|
+
if (o.id > -1) { // pinned or hover style
|
|
2733
|
+
topStyle = getSelectedFeatureStyle(baseLyr, o, opts);
|
|
2734
|
+
topIdx = ids.length;
|
|
2735
|
+
ids.push(o.id); // put the pinned/hover feature last in the render order
|
|
2736
|
+
}
|
|
2737
|
+
var style = {
|
|
2738
|
+
baseStyle: baseStyle,
|
|
2739
|
+
styler,
|
|
2740
|
+
ids,
|
|
2741
|
+
overlay: true
|
|
2742
|
+
};
|
|
2743
|
+
|
|
2744
|
+
if (layerHasCanvasDisplayStyle(baseLyr) && !opts.outlineMode) {
|
|
2745
|
+
if (geomType == 'point') {
|
|
2746
|
+
style.styler = getOverlayPointStyler(getCanvasDisplayStyle(baseLyr).styler, styler);
|
|
2747
|
+
}
|
|
2748
|
+
style.type = 'styled';
|
|
2749
|
+
}
|
|
2750
|
+
return ids.length > 0 ? style : null;
|
|
2623
2751
|
}
|
|
2624
2752
|
|
|
2625
|
-
// lyr: display layer
|
|
2626
|
-
// export function updateRectangleCoords(lyr, ids, coords) {
|
|
2627
|
-
// if (!isProjectedLayer(lyr)) return;
|
|
2628
|
-
// ids.forEach(function(id, i) {
|
|
2629
|
-
// var p = coords[i];
|
|
2630
|
-
// internal.snapVerticesToPoint([id], lyr.invertPoint(p[0], p[1]), lyr.source.dataset.arcs, true);
|
|
2631
|
-
// });
|
|
2632
|
-
// }
|
|
2633
2753
|
|
|
2634
|
-
function
|
|
2635
|
-
|
|
2636
|
-
|
|
2754
|
+
function getDefaultStyle(lyr, baseStyle) {
|
|
2755
|
+
var style = Object.assign({}, baseStyle);
|
|
2756
|
+
// reduce the dot size of large point layers
|
|
2757
|
+
if (lyr.geometry_type == 'point' && style.dotSize > 0) {
|
|
2758
|
+
style.dotSize *= getDotScale$1(lyr);
|
|
2759
|
+
}
|
|
2760
|
+
return style;
|
|
2637
2761
|
}
|
|
2638
2762
|
|
|
2639
|
-
|
|
2640
|
-
|
|
2763
|
+
function getDotScale$1(lyr) {
|
|
2764
|
+
var topTier = 10000;
|
|
2765
|
+
var n = countPoints(lyr.shapes, topTier); // short-circuit point counting above top threshold
|
|
2766
|
+
var k = n < 200 && 4 || n < 2500 && 3 || n < topTier && 2 || 1;
|
|
2767
|
+
return k;
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
function countPoints(shapes, max) {
|
|
2771
|
+
var count = 0;
|
|
2772
|
+
var i, n, shp;
|
|
2773
|
+
max = max || Infinity;
|
|
2774
|
+
for (i=0, n=shapes.length; i<n && count<max; i++) {
|
|
2775
|
+
shp = shapes[i];
|
|
2776
|
+
count += shp ? shp.length : 0;
|
|
2777
|
+
}
|
|
2778
|
+
return count;
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
|
|
2782
|
+
// style for vertex edit mode
|
|
2783
|
+
function getVertexStyle(lyr, o) {
|
|
2784
|
+
return {
|
|
2785
|
+
ids: o.ids,
|
|
2786
|
+
overlay: true,
|
|
2787
|
+
strokeColor: black,
|
|
2788
|
+
strokeWidth: 1.5,
|
|
2789
|
+
vertices: true,
|
|
2790
|
+
vertex_overlay_color: violet,
|
|
2791
|
+
vertex_overlay: o.hit_coordinates || null,
|
|
2792
|
+
selected_points: o.selected_points || null,
|
|
2793
|
+
fillColor: null
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
// style for vertex edit mode
|
|
2798
|
+
function getLineEditingStyle(lyr, o) {
|
|
2799
|
+
return {
|
|
2800
|
+
ids: o.ids,
|
|
2801
|
+
overlay: true,
|
|
2802
|
+
strokeColor: 'black',
|
|
2803
|
+
strokeWidth: 1.2,
|
|
2804
|
+
vertices: true,
|
|
2805
|
+
vertex_overlay_color: violet,
|
|
2806
|
+
vertex_overlay: o.hit_coordinates || null,
|
|
2807
|
+
selected_points: o.selected_points || null,
|
|
2808
|
+
fillColor: null
|
|
2809
|
+
};
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
|
|
2813
|
+
function getSelectedFeatureStyle(lyr, o, opts) {
|
|
2814
|
+
var isPinned = o.pinned;
|
|
2815
|
+
var inSelection = o.ids.indexOf(o.id) > -1;
|
|
2816
|
+
var geomType = lyr.geometry_type;
|
|
2817
|
+
var style;
|
|
2818
|
+
if (isPinned && opts.interactionMode == 'rectangles') {
|
|
2819
|
+
// kludge for rectangle editing mode
|
|
2820
|
+
style = selectionStyles[geomType];
|
|
2821
|
+
} else if (isPinned) {
|
|
2822
|
+
// a feature is pinned
|
|
2823
|
+
style = pinnedStyles[geomType];
|
|
2824
|
+
} else if (inSelection) {
|
|
2825
|
+
// normal hover, or hover id is in the selection set
|
|
2826
|
+
style = hoverStyles[geomType];
|
|
2827
|
+
} else {
|
|
2828
|
+
// features are selected, but hover id is not in the selection set
|
|
2829
|
+
style = unselectedHoverStyles[geomType];
|
|
2830
|
+
}
|
|
2831
|
+
return getDefaultStyle(lyr, style);
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
// Modify style to use scaled circle instead of dot symbol
|
|
2835
|
+
function getOverlayPointStyler(baseStyler, overlayStyler) {
|
|
2836
|
+
return function(obj, i) {
|
|
2837
|
+
var dotColor;
|
|
2838
|
+
var id = obj.ids ? obj.ids[i] : -1;
|
|
2839
|
+
obj.strokeWidth = 0; // kludge to support setting minimum stroke width
|
|
2840
|
+
baseStyler(obj, id);
|
|
2841
|
+
if (overlayStyler) {
|
|
2842
|
+
overlayStyler(obj, i);
|
|
2843
|
+
}
|
|
2844
|
+
dotColor = obj.dotColor;
|
|
2845
|
+
if (obj.radius && dotColor) {
|
|
2846
|
+
obj.radius += 0.4;
|
|
2847
|
+
// delete obj.fillColor; // only show outline
|
|
2848
|
+
obj.fillColor = dotColor; // comment out to only highlight stroke
|
|
2849
|
+
obj.strokeColor = dotColor;
|
|
2850
|
+
obj.strokeWidth = Math.max(obj.strokeWidth + 0.8, 1.5);
|
|
2851
|
+
obj.opacity = 1;
|
|
2852
|
+
}
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
function getCanvasDisplayStyle(lyr) {
|
|
2857
|
+
var styleIndex = {
|
|
2858
|
+
opacity: 'opacity',
|
|
2859
|
+
r: 'radius',
|
|
2860
|
+
'fill': 'fillColor',
|
|
2861
|
+
'fill-pattern': 'fillPattern',
|
|
2862
|
+
'fill-effect': 'fillEffect',
|
|
2863
|
+
'fill-opacity': 'fillOpacity',
|
|
2864
|
+
'stroke': 'strokeColor',
|
|
2865
|
+
'stroke-width': 'strokeWidth',
|
|
2866
|
+
'stroke-dasharray': 'lineDash',
|
|
2867
|
+
'stroke-opacity': 'strokeOpacity',
|
|
2868
|
+
'stroke-linecap': 'lineCap',
|
|
2869
|
+
'stroke-linejoin': 'lineJoin',
|
|
2870
|
+
'stroke-miterlimit': 'miterLimit'
|
|
2871
|
+
},
|
|
2872
|
+
// array of field names of relevant svg display properties
|
|
2873
|
+
fields = getCanvasStyleFields(lyr).filter(function(f) {return f in styleIndex;}),
|
|
2874
|
+
records = lyr.data.getRecords();
|
|
2875
|
+
var styler = function(style, i) {
|
|
2876
|
+
var rec = records[i];
|
|
2877
|
+
var fname, val;
|
|
2878
|
+
for (var j=0; j<fields.length; j++) {
|
|
2879
|
+
fname = fields[j];
|
|
2880
|
+
val = rec && rec[fname];
|
|
2881
|
+
if (val == 'none') {
|
|
2882
|
+
val = 'transparent'; // canvas equivalent of CSS 'none'
|
|
2883
|
+
}
|
|
2884
|
+
// convert svg property name to mapshaper style equivalent
|
|
2885
|
+
style[styleIndex[fname]] = val;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
if (style.strokeWidth && !style.strokeColor) {
|
|
2889
|
+
style.strokeColor = 'black';
|
|
2890
|
+
}
|
|
2891
|
+
if (!('strokeWidth' in style) && style.strokeColor) {
|
|
2892
|
+
style.strokeWidth = 1;
|
|
2893
|
+
}
|
|
2894
|
+
if (style.radius > 0 && !style.strokeWidth && !style.fillColor && lyr.geometry_type == 'point') {
|
|
2895
|
+
style.fillColor = 'black';
|
|
2896
|
+
}
|
|
2897
|
+
};
|
|
2898
|
+
return {styler: styler, type: 'styled'};
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
// check if layer should be displayed with styles
|
|
2902
|
+
function layerHasCanvasDisplayStyle(lyr) {
|
|
2903
|
+
var fields = getCanvasStyleFields(lyr);
|
|
2904
|
+
if (lyr.geometry_type == 'point') {
|
|
2905
|
+
return fields.indexOf('r') > -1; // require 'r' field for point symbols
|
|
2906
|
+
}
|
|
2907
|
+
return utils$1.difference(fields, ['opacity', 'class']).length > 0;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
|
|
2911
|
+
function getCanvasStyleFields(lyr) {
|
|
2912
|
+
var fields = lyr.data ? lyr.data.getFields() : [];
|
|
2913
|
+
return internal.findPropertiesBySymbolGeom(fields, lyr.geometry_type);
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
function flattenArcs(lyr) {
|
|
2917
|
+
lyr.source.dataset.arcs.flatten();
|
|
2918
|
+
if (isProjectedLayer(lyr)) {
|
|
2919
|
+
lyr.arcs.flatten();
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
function setZ(lyr, z) {
|
|
2924
|
+
lyr.source.dataset.arcs.setRetainedInterval(z);
|
|
2925
|
+
if (isProjectedLayer(lyr)) {
|
|
2926
|
+
lyr.arcs.setRetainedInterval(z);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
function updateZ(lyr) {
|
|
2931
|
+
if (isProjectedLayer(lyr) && !lyr.source.dataset.arcs.isFlat()) {
|
|
2932
|
+
lyr.arcs.setThresholds(lyr.source.dataset.arcs.getVertexData().zz);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
function appendNewDataRecord(layer) {
|
|
2937
|
+
if (!layer.data) return null;
|
|
2938
|
+
var fields = layer.data.getFields();
|
|
2939
|
+
var d = getEmptyDataRecord(layer.data);
|
|
2940
|
+
// TODO: handle SVG symbol layer
|
|
2941
|
+
if (internal.layerHasLabels(layer)) {
|
|
2942
|
+
d['label-text'] = 'TBD'; // without text, new labels will be invisible
|
|
2943
|
+
} else if (layer.geometry_type == 'point' && fields.includes('r')) {
|
|
2944
|
+
d.r = 3; // show a black circle if layer is styled
|
|
2945
|
+
} else if (true || layer.geometry_type == 'polyline' && fields.includes('stroke')) {
|
|
2946
|
+
d.stroke = 'black';
|
|
2947
|
+
} else if (layer.geometry_type == 'polygon' && fields.includes('stroke')) {
|
|
2948
|
+
d.stroke = 'black';
|
|
2949
|
+
}
|
|
2950
|
+
// TODO: better styling
|
|
2951
|
+
layer.data.getRecords().push(d);
|
|
2952
|
+
return d;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
function getEmptyDataRecord(table) {
|
|
2956
|
+
return table.getFields().reduce(function(memo, name) {
|
|
2957
|
+
memo[name] = null;
|
|
2958
|
+
return memo;
|
|
2959
|
+
}, {});
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
function deleteLastPath(lyr) {
|
|
2963
|
+
var arcId = lyr.arcs.size() - 1;
|
|
2964
|
+
if (lyr.layer.data) {
|
|
2965
|
+
lyr.layer.data.getRecords().pop();
|
|
2966
|
+
}
|
|
2967
|
+
var shp = lyr.layer.shapes.pop();
|
|
2968
|
+
internal.deleteLastArc(lyr.arcs);
|
|
2969
|
+
if (isProjectedLayer(lyr)) {
|
|
2970
|
+
internal.deleteLastArc(lyr.source.dataset.arcs);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
// p1, p2: two points in source data CRS coords.
|
|
2975
|
+
function appendNewPath(lyr, p1, p2) {
|
|
2976
|
+
var arcId = lyr.arcs.size();
|
|
2977
|
+
internal.appendEmptyArc(lyr.arcs);
|
|
2978
|
+
lyr.layer.shapes.push([[arcId]]);
|
|
2979
|
+
if (isProjectedLayer(lyr)) {
|
|
2980
|
+
// lyr.source.layer.shapes.push([[arcId]]);
|
|
2981
|
+
internal.appendEmptyArc(lyr.source.dataset.arcs);
|
|
2982
|
+
}
|
|
2983
|
+
appendVertex$1(lyr, p1);
|
|
2984
|
+
appendVertex$1(lyr, p2);
|
|
2985
|
+
appendNewDataRecord(lyr.layer);
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
// p: point in source data CRS coords.
|
|
2989
|
+
function insertVertex$1(lyr, id, p) {
|
|
2990
|
+
internal.insertVertex(lyr.source.dataset.arcs, id, p);
|
|
2991
|
+
if (isProjectedLayer(lyr)) {
|
|
2992
|
+
internal.insertVertex(lyr.arcs, id, lyr.projectPoint(p[0], p[1]));
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
function appendVertex$1(lyr, p) {
|
|
2997
|
+
var n = lyr.source.dataset.arcs.getPointCount();
|
|
2998
|
+
insertVertex$1(lyr, n, p);
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
// TODO: make sure we're not also removing an entire arc
|
|
3002
|
+
function deleteLastVertex(lyr) {
|
|
3003
|
+
deleteVertex$1(lyr, lyr.arcs.getPointCount() - 1);
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
function deleteVertex$1(lyr, id) {
|
|
3007
|
+
internal.deleteVertex(lyr.arcs, id);
|
|
3008
|
+
if (isProjectedLayer(lyr)) {
|
|
3009
|
+
internal.deleteVertex(lyr.source.dataset.arcs, id);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
function getLastArcCoords(target) {
|
|
3014
|
+
var arcId = target.source.dataset.arcs.size() - 1;
|
|
3015
|
+
return internal.getUnfilteredArcCoords(arcId, target.source.dataset.arcs);
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
function getLastArcLength(target) {
|
|
3019
|
+
var arcId = target.source.dataset.arcs.size() - 1;
|
|
3020
|
+
return internal.getUnfilteredArcLength(arcId, target.source.dataset.arcs);
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
function getPointCoords(lyr, fid) {
|
|
3024
|
+
return internal.cloneShape(lyr.source.layer.shapes[fid]);
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
function getVertexCoords(lyr, id) {
|
|
3028
|
+
return lyr.source.dataset.arcs.getVertex2(id);
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
// set data coords (not display coords) of one or more vertices.
|
|
3032
|
+
function setVertexCoords(lyr, ids, dataPoint) {
|
|
3033
|
+
internal.snapVerticesToPoint(ids, dataPoint, lyr.source.dataset.arcs, true);
|
|
3034
|
+
if (isProjectedLayer(lyr)) {
|
|
3035
|
+
var p = lyr.projectPoint(dataPoint[0], dataPoint[1]);
|
|
3036
|
+
internal.snapVerticesToPoint(ids, p, lyr.arcs, true);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
// coords: [x, y] point in data CRS (not display CRS)
|
|
3041
|
+
function setPointCoords(lyr, fid, coords) {
|
|
3042
|
+
lyr.source.layer.shapes[fid] = coords;
|
|
3043
|
+
if (isProjectedLayer(lyr)) {
|
|
3044
|
+
lyr.layer.shapes[fid] = projectPointCoords(coords, lyr.projectPoint);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
function updateVertexCoords(lyr, ids) {
|
|
3049
|
+
if (!isProjectedLayer(lyr)) return;
|
|
3050
|
+
var p = lyr.arcs.getVertex2(ids[0]);
|
|
3051
|
+
internal.snapVerticesToPoint(ids, lyr.invertPoint(p[0], p[1]), lyr.source.dataset.arcs, true);
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
function setRectangleCoords(lyr, ids, coords) {
|
|
3055
|
+
ids.forEach(function(id, i) {
|
|
3056
|
+
var p = coords[i];
|
|
3057
|
+
internal.snapVerticesToPoint([id], p, lyr.source.dataset.arcs, true);
|
|
3058
|
+
if (isProjectedLayer(lyr)) {
|
|
3059
|
+
internal.snapVerticesToPoint([id], lyr.projectPoint(p[0], p[1]), lyr.arcs, true);
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
// Update source data coordinates by projecting display coordinates
|
|
3065
|
+
function updatePointCoords(lyr, fid) {
|
|
2641
3066
|
if (!isProjectedLayer(lyr)) return;
|
|
2642
3067
|
var displayShp = lyr.layer.shapes[fid];
|
|
2643
3068
|
lyr.source.layer.shapes[fid] = projectPointCoords(displayShp, lyr.invertPoint);
|
|
@@ -4619,6 +5044,14 @@
|
|
|
4619
5044
|
|
|
4620
5045
|
var copyRecord = internal.copyRecord;
|
|
4621
5046
|
|
|
5047
|
+
function isUndoEvt(e) {
|
|
5048
|
+
return (e.ctrlKey || e.metaKey) && !e.shiftKey && e.key == 'z';
|
|
5049
|
+
}
|
|
5050
|
+
|
|
5051
|
+
function isRedoEvt(e) {
|
|
5052
|
+
return (e.ctrlKey || e.metaKey) && (e.shiftKey && e.key == 'z' || !e.shiftKey && e.key == 'y');
|
|
5053
|
+
}
|
|
5054
|
+
|
|
4622
5055
|
function Undo(gui) {
|
|
4623
5056
|
var history, offset, stashedUndo;
|
|
4624
5057
|
reset();
|
|
@@ -4634,14 +5067,6 @@
|
|
|
4634
5067
|
offset = 0;
|
|
4635
5068
|
}
|
|
4636
5069
|
|
|
4637
|
-
function isUndoEvt(e) {
|
|
4638
|
-
return (e.ctrlKey || e.metaKey) && !e.shiftKey && e.key == 'z';
|
|
4639
|
-
}
|
|
4640
|
-
|
|
4641
|
-
function isRedoEvt(e) {
|
|
4642
|
-
return (e.ctrlKey || e.metaKey) && (e.shiftKey && e.key == 'z' || !e.shiftKey && e.key == 'y');
|
|
4643
|
-
}
|
|
4644
|
-
|
|
4645
5070
|
function makeMultiDataSetter(ids) {
|
|
4646
5071
|
if (ids.length == 1) return makeDataSetter(ids[0]);
|
|
4647
5072
|
var target = gui.model.getActiveLayer();
|
|
@@ -4728,7 +5153,7 @@
|
|
|
4728
5153
|
|
|
4729
5154
|
gui.on('vertex_dragend', function(e) {
|
|
4730
5155
|
var target = e.data.target;
|
|
4731
|
-
var startPoint = e.
|
|
5156
|
+
var startPoint = e.point; // in data coords
|
|
4732
5157
|
var endPoint = getVertexCoords(target, e.ids[0]);
|
|
4733
5158
|
var undo = function() {
|
|
4734
5159
|
if (e.insertion) {
|
|
@@ -4758,7 +5183,27 @@
|
|
|
4758
5183
|
addHistoryState(undo, redo);
|
|
4759
5184
|
});
|
|
4760
5185
|
|
|
4761
|
-
|
|
5186
|
+
gui.on('path_add', function(e) {
|
|
5187
|
+
var redo = function() {
|
|
5188
|
+
gui.dispatchEvent('redo_path_add', {p1: e.p1, p2: e.p2});
|
|
5189
|
+
};
|
|
5190
|
+
var undo = function() {
|
|
5191
|
+
gui.dispatchEvent('undo_path_add');
|
|
5192
|
+
};
|
|
5193
|
+
addHistoryState(undo, redo);
|
|
5194
|
+
});
|
|
5195
|
+
|
|
5196
|
+
gui.on('path_extend', function(e) {
|
|
5197
|
+
var redo = function() {
|
|
5198
|
+
gui.dispatchEvent('redo_path_extend', {p: e.p});
|
|
5199
|
+
};
|
|
5200
|
+
var undo = function() {
|
|
5201
|
+
gui.dispatchEvent('undo_path_extend');
|
|
5202
|
+
};
|
|
5203
|
+
addHistoryState(undo, redo);
|
|
5204
|
+
});
|
|
5205
|
+
|
|
5206
|
+
this.clear = function() {
|
|
4762
5207
|
reset();
|
|
4763
5208
|
};
|
|
4764
5209
|
|
|
@@ -4773,21 +5218,23 @@
|
|
|
4773
5218
|
this.undo = function() {
|
|
4774
5219
|
// firing even if history is empty
|
|
4775
5220
|
// (because this event may trigger a new history state)
|
|
4776
|
-
gui.dispatchEvent('undo_redo_pre');
|
|
5221
|
+
gui.dispatchEvent('undo_redo_pre', {type: 'undo'});
|
|
4777
5222
|
var item = getHistoryItem();
|
|
4778
5223
|
if (item) {
|
|
4779
5224
|
offset++;
|
|
4780
5225
|
item.undo();
|
|
5226
|
+
gui.dispatchEvent('undo_redo_post', {type: 'undo'});
|
|
4781
5227
|
gui.dispatchEvent('map-needs-refresh');
|
|
4782
5228
|
}
|
|
4783
5229
|
};
|
|
4784
5230
|
|
|
4785
5231
|
this.redo = function() {
|
|
4786
|
-
gui.dispatchEvent('undo_redo_pre');
|
|
5232
|
+
gui.dispatchEvent('undo_redo_pre', {type: 'redo'});
|
|
4787
5233
|
if (offset <= 0) return;
|
|
4788
5234
|
offset--;
|
|
4789
5235
|
var item = getHistoryItem();
|
|
4790
5236
|
item.redo();
|
|
5237
|
+
gui.dispatchEvent('undo_redo_post', {type: 'redo'});
|
|
4791
5238
|
gui.dispatchEvent('map-needs-refresh');
|
|
4792
5239
|
};
|
|
4793
5240
|
|
|
@@ -4927,12 +5374,13 @@
|
|
|
4927
5374
|
document.addEventListener('keyup', function(e) {
|
|
4928
5375
|
if (!GUI.isActiveInstance(gui)) return;
|
|
4929
5376
|
if (e.keyCode == 16) shiftDown = false;
|
|
5377
|
+
self.dispatchEvent('keyup', getEventData(e));
|
|
4930
5378
|
});
|
|
4931
5379
|
|
|
4932
5380
|
document.addEventListener('keydown', function(e) {
|
|
4933
5381
|
if (!GUI.isActiveInstance(gui)) return;
|
|
4934
5382
|
if (e.keyCode == 16) shiftDown = true;
|
|
4935
|
-
self.dispatchEvent('keydown',
|
|
5383
|
+
self.dispatchEvent('keydown', getEventData(e));
|
|
4936
5384
|
});
|
|
4937
5385
|
|
|
4938
5386
|
this.shiftIsPressed = function() { return shiftDown; };
|
|
@@ -4947,33 +5395,37 @@
|
|
|
4947
5395
|
};
|
|
4948
5396
|
}
|
|
4949
5397
|
|
|
5398
|
+
var names = {
|
|
5399
|
+
8: 'delete',
|
|
5400
|
+
9: 'tab',
|
|
5401
|
+
13: 'enter',
|
|
5402
|
+
16: 'shift',
|
|
5403
|
+
27: 'esc',
|
|
5404
|
+
32: 'space',
|
|
5405
|
+
37: 'left',
|
|
5406
|
+
38: 'up',
|
|
5407
|
+
39: 'right',
|
|
5408
|
+
40: 'down'
|
|
5409
|
+
};
|
|
5410
|
+
|
|
5411
|
+
function getEventData(originalEvent) {
|
|
5412
|
+
var keyCode = originalEvent.keyCode;
|
|
5413
|
+
var keyName = names[keyCode] || '';
|
|
5414
|
+
return {originalEvent, keyCode, keyName};
|
|
5415
|
+
}
|
|
5416
|
+
|
|
4950
5417
|
utils$1.inherit(KeyboardEvents, EventDispatcher);
|
|
4951
5418
|
|
|
4952
5419
|
function InteractionMode(gui) {
|
|
4953
5420
|
|
|
4954
|
-
// TODO: finish this list
|
|
4955
|
-
// var modes = [{
|
|
4956
|
-
// name: 'info',
|
|
4957
|
-
// label: 'inspect features',
|
|
4958
|
-
// selection: true,
|
|
4959
|
-
// popup: true,
|
|
4960
|
-
// types: ['standard', 'polygons', 'lines', 'labels', 'points']
|
|
4961
|
-
// }, {
|
|
4962
|
-
// name: 'selection',
|
|
4963
|
-
// label: 'select features',
|
|
4964
|
-
// selection: true,
|
|
4965
|
-
// popup: true,
|
|
4966
|
-
// types: ['standard', 'polygons', 'lines', 'table', 'labels']
|
|
4967
|
-
// }]
|
|
4968
|
-
|
|
4969
5421
|
var menus = {
|
|
4970
|
-
standard: ['info', 'selection', '
|
|
4971
|
-
polygons: ['info', 'selection', '
|
|
4972
|
-
rectangles: ['info', 'selection', '
|
|
4973
|
-
lines: ['info', 'selection', '
|
|
4974
|
-
table: ['info', 'selection'
|
|
4975
|
-
labels: ['info', 'selection', '
|
|
4976
|
-
points: ['info', 'selection', '
|
|
5422
|
+
standard: ['info', 'selection', 'box'],
|
|
5423
|
+
polygons: ['info', 'selection', 'box', 'vertices'],
|
|
5424
|
+
rectangles: ['info', 'selection', 'box', 'rectangles', 'vertices'],
|
|
5425
|
+
lines: ['info', 'selection', 'box' , 'edit-lines'],
|
|
5426
|
+
table: ['info', 'selection'],
|
|
5427
|
+
labels: ['info', 'selection', 'box', 'labels', 'location', 'add-points'],
|
|
5428
|
+
points: ['info', 'selection', 'box', 'location', 'add-points']
|
|
4977
5429
|
};
|
|
4978
5430
|
|
|
4979
5431
|
var prompts = {
|
|
@@ -4993,6 +5445,7 @@
|
|
|
4993
5445
|
selection: 'select features',
|
|
4994
5446
|
'add-points': 'add points',
|
|
4995
5447
|
'draw-lines': 'draw lines',
|
|
5448
|
+
'edit-lines': 'draw/modify lines',
|
|
4996
5449
|
rectangles: 'drag-to-resize',
|
|
4997
5450
|
off: 'turn off'
|
|
4998
5451
|
};
|
|
@@ -5047,8 +5500,8 @@
|
|
|
5047
5500
|
setMode('off');
|
|
5048
5501
|
};
|
|
5049
5502
|
|
|
5050
|
-
this.
|
|
5051
|
-
return ['info', 'selection', 'data', 'labels', 'location', 'vertices', 'rectangles'].includes(mode);
|
|
5503
|
+
this.modeUsesHitDetection = function(mode) {
|
|
5504
|
+
return ['info', 'selection', 'data', 'labels', 'location', 'vertices', 'rectangles', 'edit-lines'].includes(mode);
|
|
5052
5505
|
};
|
|
5053
5506
|
|
|
5054
5507
|
this.modeUsesPopup = function(mode) {
|
|
@@ -6587,6 +7040,21 @@
|
|
|
6587
7040
|
return [xmin, ymin, xmax, ymax];
|
|
6588
7041
|
}
|
|
6589
7042
|
|
|
7043
|
+
function getUnfilteredArcLength(arcId, arcs) {
|
|
7044
|
+
var data = arcs.getVertexData();
|
|
7045
|
+
return data.nn[arcId];
|
|
7046
|
+
}
|
|
7047
|
+
|
|
7048
|
+
function getUnfilteredArcCoords(arcId, arcs) {
|
|
7049
|
+
var data = arcs.getVertexData();
|
|
7050
|
+
var coords = [];
|
|
7051
|
+
var start = data.ii[arcId];
|
|
7052
|
+
var n = data.nn[arcId];
|
|
7053
|
+
for (var i=0; i<n; i++) {
|
|
7054
|
+
coords.push([data.xx[start + i], data.yy[start + i]]);
|
|
7055
|
+
}
|
|
7056
|
+
return coords;
|
|
7057
|
+
}
|
|
6590
7058
|
|
|
6591
7059
|
function findArcIdFromVertexId(i, ii) {
|
|
6592
7060
|
// binary search
|
|
@@ -6604,6 +7072,22 @@
|
|
|
6604
7072
|
return lower; // assumes dataset is not empty
|
|
6605
7073
|
}
|
|
6606
7074
|
|
|
7075
|
+
function deleteLastArc(arcs) {
|
|
7076
|
+
var data = arcs.getVertexData();
|
|
7077
|
+
var arcId = arcs.size() - 1;
|
|
7078
|
+
var arcLen = data.nn[arcId];
|
|
7079
|
+
var n = data.xx.length;
|
|
7080
|
+
var z = arcs.getRetainedInterval();
|
|
7081
|
+
var xx2 = new Float64Array(data.xx.buffer, 0, n-arcLen);
|
|
7082
|
+
var yy2 = new Float64Array(data.yy.buffer, 0, n-arcLen);
|
|
7083
|
+
var nn2 = new Int32Array(data.nn.buffer, 0, arcs.size() - 1);
|
|
7084
|
+
var zz2 = arcs.isFlat() ?
|
|
7085
|
+
null :
|
|
7086
|
+
new Float64Array(data.zz.buffer, 0, n-arcLen);
|
|
7087
|
+
arcs.updateVertexData(nn2, xx2, yy2, zz2);
|
|
7088
|
+
arcs.setRetainedInterval(z);
|
|
7089
|
+
}
|
|
7090
|
+
|
|
6607
7091
|
function deleteVertex(arcs, i) {
|
|
6608
7092
|
var data = arcs.getVertexData();
|
|
6609
7093
|
var nn = data.nn;
|
|
@@ -6634,12 +7118,25 @@
|
|
|
6634
7118
|
arcs.setRetainedInterval(z);
|
|
6635
7119
|
}
|
|
6636
7120
|
|
|
7121
|
+
function appendEmptyArc(arcs) {
|
|
7122
|
+
var data = arcs.getVertexData();
|
|
7123
|
+
var nn = utils.extendBuffer(data.nn, data.nn.length + 1, data.nn.length);
|
|
7124
|
+
arcs.updateVertexData(nn, data.xx, data.yy, data.zz);
|
|
7125
|
+
}
|
|
7126
|
+
|
|
7127
|
+
// adds vertex to last arc
|
|
7128
|
+
// (used when adding lines in the GUI)
|
|
7129
|
+
// p: [x, y] point in display coordinates
|
|
7130
|
+
function appendVertex(arcs, p) {
|
|
7131
|
+
var i = arcs.getPointCount(); // one past the last idx
|
|
7132
|
+
insertVertex(arcs, i, p);
|
|
7133
|
+
}
|
|
7134
|
+
|
|
6637
7135
|
function insertVertex(arcs, i, p) {
|
|
6638
7136
|
var data = arcs.getVertexData();
|
|
6639
7137
|
var nn = data.nn;
|
|
6640
7138
|
var n = data.xx.length;
|
|
6641
7139
|
var count = 0;
|
|
6642
|
-
var found = false;
|
|
6643
7140
|
var xx2, yy2, zz2;
|
|
6644
7141
|
// avoid re-allocating memory on each insertion
|
|
6645
7142
|
if (data.xx.buffer.byteLength >= data.xx.length * 8 + 8) {
|
|
@@ -6652,13 +7149,21 @@
|
|
|
6652
7149
|
if (!arcs.isFlat()) {
|
|
6653
7150
|
zz2 = new Float64Array(new ArrayBuffer((n + 1) * 8), 0, n+1);
|
|
6654
7151
|
}
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
7152
|
+
if (i < 0 || i > n) {
|
|
7153
|
+
error('Out-of-range vertex insertion index:', i);
|
|
7154
|
+
} else if (i == n) {
|
|
7155
|
+
// appending vertex to last arc
|
|
7156
|
+
nn[nn.length - 1]++;
|
|
7157
|
+
} else {
|
|
7158
|
+
for (var j=0; j<nn.length; j++) {
|
|
7159
|
+
count += nn[j];
|
|
7160
|
+
if (count >= i) { // TODO: confirm this
|
|
7161
|
+
nn[j] = nn[j] + 1;
|
|
7162
|
+
break;
|
|
7163
|
+
}
|
|
6660
7164
|
}
|
|
6661
7165
|
}
|
|
7166
|
+
|
|
6662
7167
|
utils.copyElements(data.xx, 0, xx2, 0, i);
|
|
6663
7168
|
utils.copyElements(data.yy, 0, yy2, 0, i);
|
|
6664
7169
|
utils.copyElements(data.xx, i, xx2, i+1, n-i);
|
|
@@ -6728,14 +7233,16 @@
|
|
|
6728
7233
|
};
|
|
6729
7234
|
}
|
|
6730
7235
|
|
|
6731
|
-
|
|
7236
|
+
// featureFilter: optional test function, accepts feature id
|
|
7237
|
+
//
|
|
7238
|
+
function getShapeHitTest(displayLayer, ext, interactionMode, featureFilter) {
|
|
6732
7239
|
var geoType = displayLayer.layer.geometry_type;
|
|
6733
7240
|
var test;
|
|
6734
7241
|
if (geoType == 'point' && displayLayer.style.type == 'styled') {
|
|
6735
7242
|
test = getGraduatedCircleTest(getRadiusFunction(displayLayer.style));
|
|
6736
7243
|
} else if (geoType == 'point') {
|
|
6737
7244
|
test = pointTest;
|
|
6738
|
-
} else if (interactionMode == 'vertices') {
|
|
7245
|
+
} else if (interactionMode == 'vertices' || interactionMode == 'edit-lines') {
|
|
6739
7246
|
test = vertexTest;
|
|
6740
7247
|
} else if (geoType == 'polyline') {
|
|
6741
7248
|
test = polylineTest;
|
|
@@ -6800,7 +7307,8 @@
|
|
|
6800
7307
|
}
|
|
6801
7308
|
|
|
6802
7309
|
function vertexTest(x, y) {
|
|
6803
|
-
var
|
|
7310
|
+
var bufferPix = 15; // 25;
|
|
7311
|
+
var maxDist = getZoomAdjustedHitBuffer(bufferPix, 2),
|
|
6804
7312
|
cands = findHitCandidates(x, y, maxDist);
|
|
6805
7313
|
sortByDistance(x, y, cands, displayLayer.arcs);
|
|
6806
7314
|
cands = pickNearestCandidates(cands, 0, maxDist);
|
|
@@ -6916,6 +7424,9 @@
|
|
|
6916
7424
|
bbox = [];
|
|
6917
7425
|
displayLayer.layer.shapes.forEach(function(shp, shpId) {
|
|
6918
7426
|
var cand;
|
|
7427
|
+
if (featureFilter && !featureFilter(shpId)) {
|
|
7428
|
+
return;
|
|
7429
|
+
}
|
|
6919
7430
|
for (var i = 0, n = shp && shp.length; i < n; i++) {
|
|
6920
7431
|
arcs.getSimpleShapeBounds2(shp[i], bbox);
|
|
6921
7432
|
if (x + dist < bbox[0] || x - dist > bbox[2] ||
|
|
@@ -7041,12 +7552,12 @@
|
|
|
7041
7552
|
|
|
7042
7553
|
}
|
|
7043
7554
|
|
|
7044
|
-
function getPointerHitTest(mapLayer, ext, interactionMode) {
|
|
7555
|
+
function getPointerHitTest(mapLayer, ext, interactionMode, featureFilter) {
|
|
7045
7556
|
var shapeTest, targetLayer;
|
|
7046
7557
|
if (!mapLayer || !internal.layerHasGeometry(mapLayer.layer)) {
|
|
7047
7558
|
return function() {return {ids: []};};
|
|
7048
7559
|
}
|
|
7049
|
-
shapeTest = getShapeHitTest(mapLayer, ext, interactionMode);
|
|
7560
|
+
shapeTest = getShapeHitTest(mapLayer, ext, interactionMode, featureFilter);
|
|
7050
7561
|
|
|
7051
7562
|
// e: pointer event
|
|
7052
7563
|
return function(e) {
|
|
@@ -7071,6 +7582,7 @@
|
|
|
7071
7582
|
var storedData = noHitData(); // may include additional data from SVG symbol hit (e.g. hit node)
|
|
7072
7583
|
var selectionIds = [];
|
|
7073
7584
|
var transientIds = []; // e.g. hit ids while dragging a box
|
|
7585
|
+
var drawingId = -1; // kludge to allow hit detection and drawing (different feature ids)
|
|
7074
7586
|
var active = false;
|
|
7075
7587
|
var interactionMode;
|
|
7076
7588
|
var targetLayer;
|
|
@@ -7120,8 +7632,9 @@
|
|
|
7120
7632
|
updateHitTest();
|
|
7121
7633
|
};
|
|
7122
7634
|
|
|
7123
|
-
function updateHitTest() {
|
|
7124
|
-
|
|
7635
|
+
function updateHitTest(featureFilter) {
|
|
7636
|
+
if (!hoverable()) return;
|
|
7637
|
+
hitTest = getPointerHitTest(targetLayer, ext, interactionMode, featureFilter);
|
|
7125
7638
|
}
|
|
7126
7639
|
|
|
7127
7640
|
function turnOn(mode) {
|
|
@@ -7136,9 +7649,14 @@
|
|
|
7136
7649
|
active = false;
|
|
7137
7650
|
hitTest = null;
|
|
7138
7651
|
pinnedOn = false;
|
|
7652
|
+
drawingId = -1;
|
|
7139
7653
|
}
|
|
7140
7654
|
}
|
|
7141
7655
|
|
|
7656
|
+
function hoverable() {
|
|
7657
|
+
return true;
|
|
7658
|
+
}
|
|
7659
|
+
|
|
7142
7660
|
function selectable() {
|
|
7143
7661
|
return interactionMode == 'selection';
|
|
7144
7662
|
}
|
|
@@ -7148,16 +7666,20 @@
|
|
|
7148
7666
|
}
|
|
7149
7667
|
|
|
7150
7668
|
function draggable() {
|
|
7151
|
-
return interactionMode == 'vertices' || interactionMode == 'location' ||
|
|
7669
|
+
return interactionMode == 'vertices' || interactionMode == 'location' ||
|
|
7670
|
+
interactionMode == 'labels' || interactionMode == 'edit-lines';
|
|
7152
7671
|
}
|
|
7153
7672
|
|
|
7154
7673
|
function clickable() {
|
|
7155
7674
|
// click used to pin popup and select features
|
|
7156
7675
|
return interactionMode == 'data' || interactionMode == 'info' ||
|
|
7676
|
+
// interactionMode == 'edit-lines';
|
|
7157
7677
|
interactionMode == 'selection' || interactionMode == 'rectangles';
|
|
7158
7678
|
}
|
|
7159
7679
|
|
|
7160
|
-
self.getHitId = function() {
|
|
7680
|
+
self.getHitId = function() {
|
|
7681
|
+
return hitTest ? storedData.id : -1;
|
|
7682
|
+
};
|
|
7161
7683
|
|
|
7162
7684
|
// Get a reference to the active layer, so listeners to hit events can interact
|
|
7163
7685
|
// with data and shapes
|
|
@@ -7187,6 +7709,25 @@
|
|
|
7187
7709
|
}
|
|
7188
7710
|
};
|
|
7189
7711
|
|
|
7712
|
+
// manually set the selected feature id(s)
|
|
7713
|
+
// used when hit detection is turned off, e.g. 'edit-lines' mode
|
|
7714
|
+
self.setDrawingId = function(id) {
|
|
7715
|
+
if (id == drawingId) return;
|
|
7716
|
+
drawingId = id >= 0 ? id : -1;
|
|
7717
|
+
updateHitTest(function(shpId) {
|
|
7718
|
+
return shpId != id;
|
|
7719
|
+
});
|
|
7720
|
+
self.triggerChangeEvent();
|
|
7721
|
+
};
|
|
7722
|
+
|
|
7723
|
+
self.triggerChangeEvent = function() {
|
|
7724
|
+
triggerHitEvent('change');
|
|
7725
|
+
};
|
|
7726
|
+
|
|
7727
|
+
self.clearDrawingId = function() {
|
|
7728
|
+
self.setDrawingId(-1);
|
|
7729
|
+
};
|
|
7730
|
+
|
|
7190
7731
|
self.setHoverVertex = function(p, type) {
|
|
7191
7732
|
var p2 = storedData.hit_coordinates;
|
|
7192
7733
|
if (!active || !p) return;
|
|
@@ -7195,7 +7736,7 @@
|
|
|
7195
7736
|
triggerHitEvent('change');
|
|
7196
7737
|
};
|
|
7197
7738
|
|
|
7198
|
-
self.
|
|
7739
|
+
self.clearHoverVertex = function() {
|
|
7199
7740
|
if (!storedData.hit_coordinates) return;
|
|
7200
7741
|
delete storedData.hit_coordinates;
|
|
7201
7742
|
triggerHitEvent('change');
|
|
@@ -7250,7 +7791,7 @@
|
|
|
7250
7791
|
gui.on('interaction_mode_change', function(e) {
|
|
7251
7792
|
self.clearSelection();
|
|
7252
7793
|
// if (e.mode == 'off' || e.mode == 'box') {
|
|
7253
|
-
if (gui.interaction.
|
|
7794
|
+
if (gui.interaction.modeUsesHitDetection(e.mode)) {
|
|
7254
7795
|
turnOn(e.mode);
|
|
7255
7796
|
} else {
|
|
7256
7797
|
turnOff();
|
|
@@ -7270,6 +7811,7 @@
|
|
|
7270
7811
|
mouse.on('drag', handlePointerEvent, null, priority);
|
|
7271
7812
|
mouse.on('dragend', handlePointerEvent, null, priority);
|
|
7272
7813
|
|
|
7814
|
+
|
|
7273
7815
|
mouse.on('click', function(e) {
|
|
7274
7816
|
if (!hitTest || !active) return;
|
|
7275
7817
|
e.stopPropagation();
|
|
@@ -7383,6 +7925,16 @@
|
|
|
7383
7925
|
|
|
7384
7926
|
// check if an event is used in the current interaction mode
|
|
7385
7927
|
function eventIsEnabled(type) {
|
|
7928
|
+
if (!active) return false;
|
|
7929
|
+
if (interactionMode == 'edit-lines' && (type == 'hover' || type == 'dblclick')) {
|
|
7930
|
+
return true; // special case -- using hover for line drawing animation
|
|
7931
|
+
}
|
|
7932
|
+
|
|
7933
|
+
// ignore pointer events when no features are being hit
|
|
7934
|
+
// (don't block pan and other navigation when events aren't being used for editing)
|
|
7935
|
+
var hitId = self.getHitId();
|
|
7936
|
+
if (hitId == -1) return false;
|
|
7937
|
+
|
|
7386
7938
|
if (type == 'click' && !clickable()) {
|
|
7387
7939
|
return false;
|
|
7388
7940
|
}
|
|
@@ -7397,22 +7949,31 @@
|
|
|
7397
7949
|
}
|
|
7398
7950
|
|
|
7399
7951
|
function handlePointerEvent(e) {
|
|
7400
|
-
if (!hitTest || !active) return;
|
|
7401
|
-
if (self.getHitId() == -1) return; // ignore pointer events when no features are being hit
|
|
7402
|
-
// don't block pan and other navigation in modes when they are not being used
|
|
7403
7952
|
if (eventIsEnabled(e.type)) {
|
|
7404
7953
|
e.stopPropagation(); // block navigation
|
|
7405
7954
|
triggerHitEvent(e.type, e.data);
|
|
7406
7955
|
}
|
|
7407
7956
|
}
|
|
7408
7957
|
|
|
7409
|
-
//
|
|
7410
|
-
function triggerHitEvent(type,
|
|
7958
|
+
// evt: event data (may be a pointer event object, an ordinary object or null)
|
|
7959
|
+
function triggerHitEvent(type, evt) {
|
|
7960
|
+
var eventData = {
|
|
7961
|
+
mode: interactionMode,
|
|
7962
|
+
overMap: evt ? isOverMap(evt) : null
|
|
7963
|
+
};
|
|
7411
7964
|
// Merge stored hit data into the event data
|
|
7412
|
-
|
|
7965
|
+
utils$1.extend(eventData, evt || {}, storedData);
|
|
7413
7966
|
if (transientIds.length) {
|
|
7967
|
+
// add transient ids to any other hit ids
|
|
7414
7968
|
eventData.ids = utils$1.uniq(transientIds.concat(eventData.ids || []));
|
|
7415
7969
|
}
|
|
7970
|
+
// when drawing, we want the overlay layer to show the path being currently
|
|
7971
|
+
// drawn.
|
|
7972
|
+
if (drawingId >= 0) {
|
|
7973
|
+
// eventData.ids = [drawingId];
|
|
7974
|
+
// eventData.id = drawingId;
|
|
7975
|
+
eventData.ids = utils$1.uniq(eventData.ids.concat([drawingId]));
|
|
7976
|
+
}
|
|
7416
7977
|
if (pinnedOn) {
|
|
7417
7978
|
eventData.pinned = true;
|
|
7418
7979
|
}
|
|
@@ -8557,7 +9118,7 @@
|
|
|
8557
9118
|
var prevLink = El('span').addClass('popup-nav-arrow colored-text').appendTo(nav).text('◀');
|
|
8558
9119
|
var navInfo = El('span').addClass('popup-nav-info').appendTo(nav);
|
|
8559
9120
|
var nextLink = El('span').addClass('popup-nav-arrow colored-text').appendTo(nav).text('▶');
|
|
8560
|
-
var refresh
|
|
9121
|
+
var refresh;
|
|
8561
9122
|
|
|
8562
9123
|
el.addClass('rollover'); // used as a sentinel for the hover function
|
|
8563
9124
|
|
|
@@ -8567,16 +9128,15 @@
|
|
|
8567
9128
|
if (refresh) refresh();
|
|
8568
9129
|
});
|
|
8569
9130
|
|
|
8570
|
-
self.show = function(id, ids, lyr, pinned) {
|
|
8571
|
-
var singleEdit = pinned && gui.interaction.getMode() == 'data';
|
|
9131
|
+
self.show = function(id, ids, lyr, pinned, edit) {
|
|
9132
|
+
var singleEdit = edit || pinned && gui.interaction.getMode() == 'data';
|
|
8572
9133
|
var multiEdit = pinned && gui.interaction.getMode() == 'selection';
|
|
8573
9134
|
var maxHeight = parent.node().clientHeight - 36;
|
|
8574
|
-
var recIds = multiEdit ? ids : [id];
|
|
8575
9135
|
|
|
8576
9136
|
// stash a function for refreshing the current popup when data changes
|
|
8577
9137
|
// while the popup is being displayed (e.g. while dragging a label)
|
|
8578
9138
|
refresh = function() {
|
|
8579
|
-
render(
|
|
9139
|
+
render(id, ids, lyr, pinned, singleEdit || multiEdit);
|
|
8580
9140
|
};
|
|
8581
9141
|
refresh();
|
|
8582
9142
|
if (multiEdit) {
|
|
@@ -8623,7 +9183,9 @@
|
|
|
8623
9183
|
tab.show();
|
|
8624
9184
|
}
|
|
8625
9185
|
|
|
8626
|
-
function render(
|
|
9186
|
+
function render(id, ids, lyr, pinned, editable) {
|
|
9187
|
+
var recIds = id >= 0 ? [id] : ids;
|
|
9188
|
+
var el = content;
|
|
8627
9189
|
var table = lyr.data; // table can be null (e.g. if layer has no attribute data)
|
|
8628
9190
|
var tableEl = table ? renderTable(recIds, table, editable) : null;
|
|
8629
9191
|
el.empty(); // clean up if panel is already open
|
|
@@ -8647,13 +9209,18 @@
|
|
|
8647
9209
|
table && table.getFields().length > 0 ? 'feature': 'layer'));
|
|
8648
9210
|
}
|
|
8649
9211
|
|
|
9212
|
+
var footer = El('div').appendTo(el);
|
|
8650
9213
|
if (editable) {
|
|
8651
9214
|
// render "add field" button
|
|
8652
|
-
|
|
8653
|
-
El('span').addClass('add-field-btn').appendTo(line).on('click', async function(e) {
|
|
9215
|
+
El('span').addClass('add-field-btn').appendTo(footer).on('click', async function(e) {
|
|
8654
9216
|
// show "add field" dialog
|
|
8655
9217
|
openAddFieldPopup(gui, recIds, lyr);
|
|
8656
9218
|
}).text('+ add field');
|
|
9219
|
+
} else if (pinned) {
|
|
9220
|
+
// render "Click to edit" button
|
|
9221
|
+
El('span').addClass('edit-data-btn').appendTo(footer).on('click', async function(e) {
|
|
9222
|
+
self.show(id, ids, lyr, true, true);
|
|
9223
|
+
}).text('▸ click to edit');
|
|
8657
9224
|
}
|
|
8658
9225
|
}
|
|
8659
9226
|
|
|
@@ -9108,19 +9675,27 @@
|
|
|
9108
9675
|
}
|
|
9109
9676
|
|
|
9110
9677
|
// pointer thresholds for hovering near a vertex or segment midpoint
|
|
9111
|
-
var HOVER_THRESHOLD = 8;
|
|
9112
|
-
var MIDPOINT_THRESHOLD = 11;
|
|
9678
|
+
var HOVER_THRESHOLD$1 = 8;
|
|
9679
|
+
var MIDPOINT_THRESHOLD$1 = 11;
|
|
9113
9680
|
|
|
9114
|
-
function
|
|
9115
|
-
var insertionPoint;
|
|
9116
|
-
var
|
|
9681
|
+
function initLineEditing(gui, ext, hit) {
|
|
9682
|
+
var insertionPoint; // not used in this mode
|
|
9683
|
+
var dragVertexInfo;
|
|
9684
|
+
var hoverVertexInfo;
|
|
9685
|
+
var prevClickEvent;
|
|
9686
|
+
var prevHoverEvent;
|
|
9687
|
+
var drawingId = -1; // feature id of path being drawn
|
|
9117
9688
|
|
|
9118
9689
|
function active() {
|
|
9119
|
-
return gui.interaction.getMode() == '
|
|
9690
|
+
return gui.interaction.getMode() == 'edit-lines';
|
|
9120
9691
|
}
|
|
9121
9692
|
|
|
9122
9693
|
function dragging() {
|
|
9123
|
-
return active() && !!
|
|
9694
|
+
return active() && !!dragVertexInfo;
|
|
9695
|
+
}
|
|
9696
|
+
|
|
9697
|
+
function drawing() {
|
|
9698
|
+
return drawingId > -1;
|
|
9124
9699
|
}
|
|
9125
9700
|
|
|
9126
9701
|
function setHoverVertex(id) {
|
|
@@ -9129,42 +9704,69 @@
|
|
|
9129
9704
|
}
|
|
9130
9705
|
|
|
9131
9706
|
function clearHoverVertex() {
|
|
9132
|
-
hit.
|
|
9707
|
+
hit.clearHoverVertex();
|
|
9708
|
+
hoverVertexInfo = null;
|
|
9133
9709
|
}
|
|
9134
9710
|
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9711
|
+
gui.on('interaction_mode_change', function(e) {
|
|
9712
|
+
gui.container.findChild('.map-layers').classed('edit-lines', e.mode == 'edit-lines');
|
|
9713
|
+
if (e.mode == 'edit-lines') {
|
|
9714
|
+
turnOn();
|
|
9715
|
+
} else {
|
|
9716
|
+
turnOff();
|
|
9717
|
+
}
|
|
9718
|
+
}, null, 10); // higher priority than hit control, so turnOff() has correct hit target
|
|
9719
|
+
|
|
9720
|
+
gui.on('redo_path_add', function(e) {
|
|
9139
9721
|
var target = hit.getHitTarget();
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9722
|
+
clearDrawingInfo();
|
|
9723
|
+
appendNewPath(target, e.p1, e.p2);
|
|
9724
|
+
deleteLastVertex(target); // second vertex is a placeholder
|
|
9725
|
+
gui.undo.redo(); // add next vertex in the path
|
|
9726
|
+
gui.model.updated({arc_count: true});
|
|
9727
|
+
});
|
|
9728
|
+
|
|
9729
|
+
gui.on('undo_path_add', function(e) {
|
|
9730
|
+
deleteLastPath(hit.getHitTarget());
|
|
9731
|
+
clearDrawingInfo();
|
|
9732
|
+
});
|
|
9733
|
+
|
|
9734
|
+
gui.on('redo_path_extend', function(e) {
|
|
9735
|
+
var target = hit.getHitTarget();
|
|
9736
|
+
if (drawing() && prevHoverEvent) {
|
|
9737
|
+
updatePathEndpoint(e.p);
|
|
9738
|
+
appendVertex$1(target, pixToDataCoords(prevHoverEvent.x, prevHoverEvent.y));
|
|
9739
|
+
} else {
|
|
9740
|
+
appendVertex$1(target, e.p);
|
|
9148
9741
|
}
|
|
9149
|
-
|
|
9150
|
-
return getVertexCoords(target, i); // data coordinates
|
|
9151
|
-
});
|
|
9152
|
-
return {
|
|
9153
|
-
target: target,
|
|
9154
|
-
ids: nearestIds,
|
|
9155
|
-
points: points
|
|
9156
|
-
};
|
|
9157
|
-
}
|
|
9742
|
+
});
|
|
9158
9743
|
|
|
9159
|
-
function
|
|
9744
|
+
gui.on('undo_path_extend', function(e) {
|
|
9160
9745
|
var target = hit.getHitTarget();
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9746
|
+
if (drawing() && prevHoverEvent) {
|
|
9747
|
+
deleteLastVertex(target);
|
|
9748
|
+
updatePathEndpoint(pixToDataCoords(prevHoverEvent.x, prevHoverEvent.y));
|
|
9749
|
+
} else {
|
|
9750
|
+
deleteLastVertex(target);
|
|
9751
|
+
}
|
|
9752
|
+
if (getLastArcLength(target) < 2) {
|
|
9753
|
+
gui.undo.undo(); // remove the path
|
|
9754
|
+
}
|
|
9755
|
+
});
|
|
9756
|
+
|
|
9757
|
+
function turnOn() {}
|
|
9758
|
+
|
|
9759
|
+
function turnOff() {
|
|
9760
|
+
finishPath();
|
|
9761
|
+
clearDrawingInfo();
|
|
9762
|
+
insertionPoint = null;
|
|
9763
|
+
}
|
|
9764
|
+
|
|
9765
|
+
function clearDrawingInfo() {
|
|
9766
|
+
hit.clearDrawingId();
|
|
9767
|
+
drawingId = -1;
|
|
9768
|
+
dragVertexInfo = hoverVertexInfo = null;
|
|
9769
|
+
prevClickEvent = prevHoverEvent = null;
|
|
9168
9770
|
}
|
|
9169
9771
|
|
|
9170
9772
|
hit.on('dragstart', function(e) {
|
|
@@ -9172,30 +9774,30 @@
|
|
|
9172
9774
|
if (insertionPoint) {
|
|
9173
9775
|
var target = hit.getHitTarget();
|
|
9174
9776
|
insertVertex$1(target, insertionPoint.i, insertionPoint.point);
|
|
9175
|
-
|
|
9777
|
+
dragVertexInfo = {
|
|
9176
9778
|
target: target,
|
|
9177
9779
|
insertion: true,
|
|
9178
|
-
|
|
9179
|
-
|
|
9780
|
+
point: insertionPoint.point,
|
|
9781
|
+
ids: [insertionPoint.i]
|
|
9180
9782
|
};
|
|
9181
9783
|
insertionPoint = null;
|
|
9182
|
-
} else {
|
|
9183
|
-
|
|
9784
|
+
} else if (!drawing()) {
|
|
9785
|
+
dragVertexInfo = findDraggableVertices(e);
|
|
9184
9786
|
}
|
|
9185
|
-
if (
|
|
9186
|
-
setHoverVertex(
|
|
9787
|
+
if (dragVertexInfo) {
|
|
9788
|
+
setHoverVertex(dragVertexInfo.ids[0]);
|
|
9187
9789
|
}
|
|
9188
9790
|
});
|
|
9189
9791
|
|
|
9190
9792
|
hit.on('drag', function(e) {
|
|
9191
|
-
if (!dragging()) return;
|
|
9793
|
+
if (!dragging() || drawing()) return;
|
|
9192
9794
|
var target = hit.getHitTarget();
|
|
9193
9795
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
9194
9796
|
if (gui.keyboard.shiftIsPressed()) {
|
|
9195
|
-
internal.snapPointToArcEndpoint(p,
|
|
9797
|
+
internal.snapPointToArcEndpoint(p, dragVertexInfo.ids, target.arcs);
|
|
9196
9798
|
}
|
|
9197
|
-
internal.snapVerticesToPoint(
|
|
9198
|
-
setHoverVertex(
|
|
9799
|
+
internal.snapVerticesToPoint(dragVertexInfo.ids, p, target.arcs);
|
|
9800
|
+
setHoverVertex(dragVertexInfo.ids[0]);
|
|
9199
9801
|
// redrawing the whole map updates the data layer as well as the overlay layer
|
|
9200
9802
|
// gui.dispatchEvent('map-needs-refresh');
|
|
9201
9803
|
});
|
|
@@ -9205,18 +9807,112 @@
|
|
|
9205
9807
|
// kludge to get dataset to recalculate internal bounding boxes
|
|
9206
9808
|
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
9207
9809
|
clearHoverVertex();
|
|
9208
|
-
updateVertexCoords(
|
|
9209
|
-
gui.dispatchEvent('vertex_dragend',
|
|
9810
|
+
updateVertexCoords(dragVertexInfo.target, dragVertexInfo.ids);
|
|
9811
|
+
gui.dispatchEvent('vertex_dragend', dragVertexInfo);
|
|
9210
9812
|
gui.dispatchEvent('map-needs-refresh');
|
|
9211
|
-
|
|
9813
|
+
dragVertexInfo = null;
|
|
9212
9814
|
});
|
|
9213
9815
|
|
|
9816
|
+
// shift + double-click deletes a vertex (when not drawing)
|
|
9817
|
+
// double-click finishes a path (when drawing)
|
|
9214
9818
|
hit.on('dblclick', function(e) {
|
|
9215
9819
|
if (!active()) return;
|
|
9216
|
-
|
|
9820
|
+
if (drawing()) {
|
|
9821
|
+
// double click finishes a path
|
|
9822
|
+
// before: dblclick is preceded by two clicks, need another vertex delete
|
|
9823
|
+
// now: second click is suppressed
|
|
9824
|
+
// deleteLastVertex(hit.getHitTarget());
|
|
9825
|
+
finishPath();
|
|
9826
|
+
e.stopPropagation(); // prevent dblclick zoom
|
|
9827
|
+
return;
|
|
9828
|
+
}
|
|
9829
|
+
});
|
|
9830
|
+
|
|
9831
|
+
// hover event highlights the nearest point in close proximity to the pointer
|
|
9832
|
+
// ... or the closest segment midpoint (for adding a new vertex)
|
|
9833
|
+
hit.on('hover', function(e) {
|
|
9834
|
+
if (!active() || dragging()) return;
|
|
9835
|
+
if (drawing() && !e.overMap) {
|
|
9836
|
+
finishPath();
|
|
9837
|
+
return;
|
|
9838
|
+
}
|
|
9839
|
+
if (drawing()) {
|
|
9840
|
+
if (gui.keyboard.shiftIsPressed()) {
|
|
9841
|
+
alignPointerPosition(e, prevClickEvent);
|
|
9842
|
+
}
|
|
9843
|
+
updatePathEndpoint(pixToDataCoords(e.x, e.y));
|
|
9844
|
+
|
|
9845
|
+
// highlight nearby snappable vertex (the closest vertex on a nearby line,
|
|
9846
|
+
// or the first vertex of the current drawing path if not near a line)
|
|
9847
|
+
hoverVertexInfo = e.id >= 0 && findDraggableVertices(e) || findPathStartInfo(e);
|
|
9848
|
+
if (hoverVertexInfo) {
|
|
9849
|
+
// hovering near a vertex: highlight the vertex
|
|
9850
|
+
setHoverVertex(hoverVertexInfo.ids[0]);
|
|
9851
|
+
} else {
|
|
9852
|
+
clearHoverVertex();
|
|
9853
|
+
}
|
|
9854
|
+
prevHoverEvent = e;
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
if (e.id >= 0 === false) {
|
|
9858
|
+
// pointer is not near a path
|
|
9859
|
+
return;
|
|
9860
|
+
}
|
|
9861
|
+
hoverVertexInfo = findDraggableVertices(e);
|
|
9862
|
+
insertionPoint = hoverVertexInfo ? null : findMidpointInsertionPoint(e);
|
|
9863
|
+
if (hoverVertexInfo) {
|
|
9864
|
+
// hovering near a vertex: highlight the vertex
|
|
9865
|
+
setHoverVertex(hoverVertexInfo.ids[0]);
|
|
9866
|
+
} else if (insertionPoint) {
|
|
9867
|
+
// hovering near a segment midpoint: highlight the midpoint
|
|
9868
|
+
hit.setHoverVertex(insertionPoint.displayPoint);
|
|
9869
|
+
} else {
|
|
9870
|
+
// pointer is not over a vertex: clear any hover effect
|
|
9871
|
+
clearHoverVertex();
|
|
9872
|
+
}
|
|
9873
|
+
}, null, 100);
|
|
9874
|
+
|
|
9875
|
+
// click starts or extends a new path
|
|
9876
|
+
hit.on('click', function(e) {
|
|
9877
|
+
if (!active()) return;
|
|
9878
|
+
if (detectDoubleClick(e)) return; // ignore second click of a dblclick
|
|
9879
|
+
var p = pixToDataCoords(e.x, e.y);
|
|
9880
|
+
if (drawing() && hoverVertexInfo) {
|
|
9881
|
+
// finish the path if a vertex is highlighted
|
|
9882
|
+
p = hoverVertexInfo.point;
|
|
9883
|
+
extendPath(p);
|
|
9884
|
+
finishPath();
|
|
9885
|
+
} else if (drawing()) {
|
|
9886
|
+
extendPath(p);
|
|
9887
|
+
} else if (gui.keyboard.shiftIsPressed()) {
|
|
9888
|
+
deleteActiveVertex(e);
|
|
9889
|
+
} else {
|
|
9890
|
+
startPath(p);
|
|
9891
|
+
}
|
|
9892
|
+
prevClickEvent = e;
|
|
9893
|
+
});
|
|
9894
|
+
|
|
9895
|
+
// esc key finishes a path
|
|
9896
|
+
gui.keyboard.on('keydown', function(e) {
|
|
9897
|
+
if (active() && e.keyName == 'esc') {
|
|
9898
|
+
finishPath();
|
|
9899
|
+
}
|
|
9900
|
+
});
|
|
9901
|
+
|
|
9902
|
+
function detectDoubleClick(evt) {
|
|
9903
|
+
if (!prevClickEvent) return false;
|
|
9904
|
+
var elapsed = evt.time - prevClickEvent.time;
|
|
9905
|
+
var dx = Math.abs(evt.x - prevClickEvent.x);
|
|
9906
|
+
var dy = Math.abs(evt.y - prevClickEvent.y);
|
|
9907
|
+
var dbl = elapsed < 500 && dx <= 2 && dy <= 2;
|
|
9908
|
+
return dbl;
|
|
9909
|
+
}
|
|
9910
|
+
|
|
9911
|
+
function deleteActiveVertex(e) {
|
|
9912
|
+
var info = findDraggableVertices(e);
|
|
9217
9913
|
if (!info) return;
|
|
9218
|
-
var target = hit.getHitTarget();
|
|
9219
9914
|
var vId = info.ids[0];
|
|
9915
|
+
var target = hit.getHitTarget();
|
|
9220
9916
|
if (internal.vertexIsArcStart(vId, target.arcs) ||
|
|
9221
9917
|
internal.vertexIsArcEnd(vId, target.arcs)) {
|
|
9222
9918
|
// TODO: support removing arc endpoints
|
|
@@ -9229,415 +9925,178 @@
|
|
|
9229
9925
|
deleteVertex$1(target, vId);
|
|
9230
9926
|
clearHoverVertex();
|
|
9231
9927
|
gui.dispatchEvent('map-needs-refresh');
|
|
9232
|
-
}
|
|
9928
|
+
}
|
|
9233
9929
|
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9930
|
+
function pixToDataCoords(x, y) {
|
|
9931
|
+
var target = hit.getHitTarget();
|
|
9932
|
+
return translateDisplayPoint(target, ext.translatePixelCoords(x, y));
|
|
9933
|
+
}
|
|
9934
|
+
|
|
9935
|
+
// Change the x, y pixel location of thisEvt so that the segment extending
|
|
9936
|
+
// from prevEvt is aligned to one of 8 angles.
|
|
9937
|
+
function alignPointerPosition(thisEvt, prevEvt) {
|
|
9938
|
+
if (!prevEvt) return;
|
|
9939
|
+
var x0 = prevEvt.x;
|
|
9940
|
+
var y0 = prevEvt.y;
|
|
9941
|
+
var dist = geom.distance2D(thisEvt.x, thisEvt.y, x0, y0);
|
|
9942
|
+
var dist2 = dist / Math.sqrt(2);
|
|
9943
|
+
if (dist < 1) return;
|
|
9944
|
+
var minDist = Infinity;
|
|
9945
|
+
var cands = [
|
|
9946
|
+
{x: x0, y: y0 + dist},
|
|
9947
|
+
{x: x0, y: y0 - dist},
|
|
9948
|
+
{x: x0 + dist, y: y0},
|
|
9949
|
+
{x: x0 - dist, y: y0},
|
|
9950
|
+
{x: x0 + dist2, y: y0 + dist2},
|
|
9951
|
+
{x: x0 + dist2, y: y0 - dist2},
|
|
9952
|
+
{x: x0 - dist2, y: y0 + dist2},
|
|
9953
|
+
{x: x0 - dist2, y: y0 - dist2}
|
|
9954
|
+
];
|
|
9955
|
+
var snapped = cands.reduce(function(memo, cand) {
|
|
9956
|
+
var dist = geom.distance2D(thisEvt.x, thisEvt.y, cand.x, cand.y);
|
|
9957
|
+
if (dist < minDist) {
|
|
9958
|
+
minDist = dist;
|
|
9959
|
+
return cand;
|
|
9960
|
+
}
|
|
9961
|
+
return memo;
|
|
9962
|
+
}, null);
|
|
9963
|
+
thisEvt.x = snapped.x;
|
|
9964
|
+
thisEvt.y = snapped.y;
|
|
9965
|
+
}
|
|
9966
|
+
|
|
9967
|
+
function finishPath() {
|
|
9968
|
+
if (!drawing()) return;
|
|
9969
|
+
var target = hit.getHitTarget();
|
|
9970
|
+
if (getLastArcLength(target) <= 2) { // includes hover point
|
|
9971
|
+
deleteLastPath(target);
|
|
9248
9972
|
} else {
|
|
9249
|
-
|
|
9250
|
-
clearHoverVertex();
|
|
9973
|
+
deleteLastVertex(target);
|
|
9251
9974
|
}
|
|
9252
|
-
|
|
9253
|
-
|
|
9975
|
+
clearDrawingInfo();
|
|
9976
|
+
gui.model.updated({arc_count: true});
|
|
9977
|
+
}
|
|
9254
9978
|
|
|
9979
|
+
function startPath(p) {
|
|
9980
|
+
var target = hit.getHitTarget();
|
|
9981
|
+
var p1 = hoverVertexInfo ? getVertexCoords(target, hoverVertexInfo.ids[0]) : p;
|
|
9982
|
+
var p2 = p;
|
|
9983
|
+
appendNewPath(target, p1, p2);
|
|
9984
|
+
gui.dispatchEvent('path_add', {target, p1, p2});
|
|
9985
|
+
drawingId = target.layer.shapes.length - 1;
|
|
9986
|
+
hit.setDrawingId(drawingId);
|
|
9987
|
+
}
|
|
9255
9988
|
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
y1 = yy[i],
|
|
9265
|
-
x2 = xx[j],
|
|
9266
|
-
y2 = yy[j],
|
|
9267
|
-
cx = (x1 + x2) / 2,
|
|
9268
|
-
cy = (y1 + y2) / 2,
|
|
9269
|
-
midpoint = [cx, cy],
|
|
9270
|
-
dist = geom.distance2D(cx, cy, p[0], p[1]);
|
|
9271
|
-
if (dist < minDist) {
|
|
9272
|
-
minDist = dist;
|
|
9273
|
-
v = {
|
|
9274
|
-
i: (i < j ? i : j) + 1, // insertion point
|
|
9275
|
-
segment: [i, j],
|
|
9276
|
-
segmentLen: geom.distance2D(x1, y1, x2, y2),
|
|
9277
|
-
displayPoint: midpoint,
|
|
9278
|
-
point: translateDisplayPoint(target, midpoint),
|
|
9279
|
-
distance: dist
|
|
9280
|
-
};
|
|
9989
|
+
function extendPath(p) {
|
|
9990
|
+
var target = hit.getHitTarget();
|
|
9991
|
+
var len = getLastArcLength(target);
|
|
9992
|
+
if (false && len == 2) {
|
|
9993
|
+
var pathCoords = getLastArcCoords(target);
|
|
9994
|
+
gui.dispatchEvent('path_add', {target, p1: pathCoords[0], p2: pathCoords[1]});
|
|
9995
|
+
} else if (len >= 2) {
|
|
9996
|
+
gui.dispatchEvent('path_extend', {target, p});
|
|
9281
9997
|
}
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
function initInteractiveEditing(gui, ext, hit) {
|
|
9287
|
-
initLabelDragging(gui, ext, hit);
|
|
9288
|
-
initPointDragging(gui, ext, hit);
|
|
9289
|
-
initVertexDragging(gui, ext, hit);
|
|
9998
|
+
appendVertex$1(target, p);
|
|
9999
|
+
hit.triggerChangeEvent();
|
|
10000
|
+
}
|
|
9290
10001
|
|
|
9291
|
-
//
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
10002
|
+
// p: [x, y] source data coordinates
|
|
10003
|
+
function updatePathEndpoint(p) {
|
|
10004
|
+
var target = hit.getHitTarget();
|
|
10005
|
+
var i = target.arcs.getPointCount() - 1;
|
|
10006
|
+
if (hoverVertexInfo) {
|
|
10007
|
+
p = getVertexCoords(target, hoverVertexInfo.ids[0]); // snap to selected point
|
|
10008
|
+
}
|
|
10009
|
+
setVertexCoords(target, [i], p);
|
|
10010
|
+
hit.triggerChangeEvent();
|
|
10011
|
+
}
|
|
9299
10012
|
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
dotColor: "#223",
|
|
9314
|
-
dotSize: 1
|
|
9315
|
-
},
|
|
9316
|
-
activeStyleDarkMode = {
|
|
9317
|
-
type: 'outline',
|
|
9318
|
-
strokeColors: [lightStroke, 'white'],
|
|
9319
|
-
strokeWidth: 0.9,
|
|
9320
|
-
dotColor: 'white',
|
|
9321
|
-
dotSize: 1
|
|
9322
|
-
},
|
|
9323
|
-
activeStyleForLabels = {
|
|
9324
|
-
dotColor: "rgba(250, 0, 250, 0.45)", // violet dot with transparency
|
|
9325
|
-
dotSize: 1
|
|
9326
|
-
},
|
|
9327
|
-
referenceStyle = { // outline style for reference layers
|
|
9328
|
-
type: 'outline',
|
|
9329
|
-
strokeColors: [null, '#78c110'], // upped saturation from #86c927
|
|
9330
|
-
strokeWidth: 0.85,
|
|
9331
|
-
dotColor: "#73ba20",
|
|
9332
|
-
dotSize: 1
|
|
9333
|
-
},
|
|
9334
|
-
intersectionStyle = {
|
|
9335
|
-
dotColor: "#F24400",
|
|
9336
|
-
dotSize: 1
|
|
9337
|
-
},
|
|
9338
|
-
hoverStyles = {
|
|
9339
|
-
polygon: {
|
|
9340
|
-
fillColor: hoverFill,
|
|
9341
|
-
strokeColor: black,
|
|
9342
|
-
strokeWidth: 1.2
|
|
9343
|
-
}, point: {
|
|
9344
|
-
dotColor: violet, // black,
|
|
9345
|
-
dotSize: 2.5
|
|
9346
|
-
}, polyline: {
|
|
9347
|
-
strokeColor: black,
|
|
9348
|
-
strokeWidth: 2.5,
|
|
9349
|
-
}
|
|
9350
|
-
},
|
|
9351
|
-
unselectedHoverStyles = {
|
|
9352
|
-
polygon: {
|
|
9353
|
-
fillColor: 'rgba(0,0,0,0)',
|
|
9354
|
-
strokeColor: black,
|
|
9355
|
-
strokeWidth: 1.2
|
|
9356
|
-
}, point: {
|
|
9357
|
-
dotColor: black, // grey,
|
|
9358
|
-
dotSize: 2
|
|
9359
|
-
}, polyline: {
|
|
9360
|
-
strokeColor: black, // grey,
|
|
9361
|
-
strokeWidth: 2.5
|
|
9362
|
-
}
|
|
9363
|
-
},
|
|
9364
|
-
selectionStyles = {
|
|
9365
|
-
polygon: {
|
|
9366
|
-
fillColor: hoverFill,
|
|
9367
|
-
strokeColor: black,
|
|
9368
|
-
strokeWidth: 1.2
|
|
9369
|
-
}, point: {
|
|
9370
|
-
dotColor: violet, // black,
|
|
9371
|
-
dotSize: 1.5
|
|
9372
|
-
}, polyline: {
|
|
9373
|
-
strokeColor: violet, // black,
|
|
9374
|
-
strokeWidth: 2.5
|
|
9375
|
-
}
|
|
9376
|
-
},
|
|
9377
|
-
// not used
|
|
9378
|
-
selectionHoverStyles = {
|
|
9379
|
-
polygon: {
|
|
9380
|
-
fillColor: selectionFill,
|
|
9381
|
-
strokeColor: black,
|
|
9382
|
-
strokeWidth: 1.2
|
|
9383
|
-
}, point: {
|
|
9384
|
-
dotColor: black,
|
|
9385
|
-
dotSize: 1.5
|
|
9386
|
-
}, polyline: {
|
|
9387
|
-
strokeColor: black,
|
|
9388
|
-
strokeWidth: 2
|
|
9389
|
-
}
|
|
9390
|
-
},
|
|
9391
|
-
pinnedStyles = {
|
|
9392
|
-
polygon: {
|
|
9393
|
-
fillColor: violetFill,
|
|
9394
|
-
strokeColor: violet,
|
|
9395
|
-
strokeWidth: 1.8
|
|
9396
|
-
}, point: {
|
|
9397
|
-
dotColor: violet,
|
|
9398
|
-
dotSize: 3
|
|
9399
|
-
}, polyline: {
|
|
9400
|
-
strokeColor: violet, // black, // violet,
|
|
9401
|
-
strokeWidth: 3
|
|
9402
|
-
}
|
|
9403
|
-
};
|
|
9404
|
-
|
|
9405
|
-
function getIntersectionStyle(lyr, opts) {
|
|
9406
|
-
return getDefaultStyle(lyr, intersectionStyle);
|
|
9407
|
-
}
|
|
9408
|
-
|
|
9409
|
-
// Style for unselected layers with visibility turned on
|
|
9410
|
-
// (styled layers have)
|
|
9411
|
-
function getReferenceLayerStyle(lyr, opts) {
|
|
9412
|
-
var style;
|
|
9413
|
-
if (layerHasCanvasDisplayStyle(lyr) && !opts.outlineMode) {
|
|
9414
|
-
style = getCanvasDisplayStyle(lyr);
|
|
9415
|
-
} else if (internal.layerHasLabels(lyr) && !opts.outlineMode) {
|
|
9416
|
-
style = {dotSize: 0}; // no reference dots if labels are visible
|
|
9417
|
-
} else {
|
|
9418
|
-
style = getDefaultStyle(lyr, referenceStyle);
|
|
9419
|
-
}
|
|
9420
|
-
return style;
|
|
9421
|
-
}
|
|
9422
|
-
|
|
9423
|
-
function getActiveLayerStyle(lyr, opts) {
|
|
9424
|
-
var style;
|
|
9425
|
-
if (layerHasCanvasDisplayStyle(lyr) && !opts.outlineMode) {
|
|
9426
|
-
style = getCanvasDisplayStyle(lyr);
|
|
9427
|
-
} else if (internal.layerHasLabels(lyr) && !opts.outlineMode) {
|
|
9428
|
-
style = getDefaultStyle(lyr, activeStyleForLabels);
|
|
9429
|
-
} else if (opts.darkMode) {
|
|
9430
|
-
style = getDefaultStyle(lyr, activeStyleDarkMode);
|
|
9431
|
-
} else {
|
|
9432
|
-
style = getDefaultStyle(lyr, activeStyle);
|
|
9433
|
-
}
|
|
9434
|
-
return style;
|
|
9435
|
-
}
|
|
9436
|
-
|
|
9437
|
-
// Returns a display style for the overlay layer.
|
|
9438
|
-
// The overlay layer renders several kinds of feature, each of which is displayed
|
|
9439
|
-
// with a different style.
|
|
9440
|
-
//
|
|
9441
|
-
// * hover shapes
|
|
9442
|
-
// * selected shapes
|
|
9443
|
-
// * pinned shapes
|
|
9444
|
-
//
|
|
9445
|
-
function getOverlayStyle(baseLyr, o, opts) {
|
|
9446
|
-
if (opts.interactionMode == 'vertices') {
|
|
9447
|
-
return getVertexStyle(baseLyr, o);
|
|
9448
|
-
}
|
|
9449
|
-
var geomType = baseLyr.geometry_type;
|
|
9450
|
-
var topId = o.id; // pinned id (if pinned) or hover id
|
|
9451
|
-
var topIdx = -1;
|
|
9452
|
-
var styler = function(style, i) {
|
|
9453
|
-
var defaultStyle = i === topIdx ? topStyle : outlineStyle;
|
|
9454
|
-
if (baseStyle.styler) {
|
|
9455
|
-
// TODO: render default stroke widths without scaling
|
|
9456
|
-
// (will need to pass symbol scale to the styler function)
|
|
9457
|
-
style.strokeWidth = defaultStyle.strokeWidth;
|
|
9458
|
-
baseStyle.styler(style, i); // get styled stroke width (if set)
|
|
9459
|
-
style.strokeColor = defaultStyle.strokeColor;
|
|
9460
|
-
style.fillColor = defaultStyle.fillColor;
|
|
9461
|
-
} else {
|
|
9462
|
-
Object.assign(style, defaultStyle);
|
|
9463
|
-
}
|
|
9464
|
-
};
|
|
9465
|
-
var baseStyle = getActiveLayerStyle(baseLyr, opts);
|
|
9466
|
-
var outlineStyle = getDefaultStyle(baseLyr, selectionStyles[geomType]);
|
|
9467
|
-
var topStyle;
|
|
9468
|
-
var ids = o.ids.filter(function(i) {
|
|
9469
|
-
return i != o.id; // move selected id to the end
|
|
9470
|
-
});
|
|
9471
|
-
if (o.id > -1) { // pinned or hover style
|
|
9472
|
-
topStyle = getSelectedFeatureStyle(baseLyr, o, opts);
|
|
9473
|
-
topIdx = ids.length;
|
|
9474
|
-
ids.push(o.id); // put the pinned/hover feature last in the render order
|
|
9475
|
-
}
|
|
9476
|
-
var style = {
|
|
9477
|
-
baseStyle: baseStyle,
|
|
9478
|
-
styler,
|
|
9479
|
-
ids,
|
|
9480
|
-
overlay: true
|
|
9481
|
-
};
|
|
9482
|
-
|
|
9483
|
-
if (layerHasCanvasDisplayStyle(baseLyr) && !opts.outlineMode) {
|
|
9484
|
-
if (geomType == 'point') {
|
|
9485
|
-
style.styler = getOverlayPointStyler(getCanvasDisplayStyle(baseLyr).styler, styler);
|
|
9486
|
-
}
|
|
9487
|
-
style.type = 'styled';
|
|
9488
|
-
}
|
|
9489
|
-
return ids.length > 0 ? style : null;
|
|
9490
|
-
}
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
function getDefaultStyle(lyr, baseStyle) {
|
|
9494
|
-
var style = Object.assign({}, baseStyle);
|
|
9495
|
-
// reduce the dot size of large point layers
|
|
9496
|
-
if (lyr.geometry_type == 'point' && style.dotSize > 0) {
|
|
9497
|
-
style.dotSize *= getDotScale$1(lyr);
|
|
9498
|
-
}
|
|
9499
|
-
return style;
|
|
9500
|
-
}
|
|
9501
|
-
|
|
9502
|
-
function getDotScale$1(lyr) {
|
|
9503
|
-
var topTier = 10000;
|
|
9504
|
-
var n = countPoints(lyr.shapes, topTier); // short-circuit point counting above top threshold
|
|
9505
|
-
var k = n < 200 && 4 || n < 2500 && 3 || n < topTier && 2 || 1;
|
|
9506
|
-
return k;
|
|
9507
|
-
}
|
|
9508
|
-
|
|
9509
|
-
function countPoints(shapes, max) {
|
|
9510
|
-
var count = 0;
|
|
9511
|
-
var i, n, shp;
|
|
9512
|
-
max = max || Infinity;
|
|
9513
|
-
for (i=0, n=shapes.length; i<n && count<max; i++) {
|
|
9514
|
-
shp = shapes[i];
|
|
9515
|
-
count += shp ? shp.length : 0;
|
|
9516
|
-
}
|
|
9517
|
-
return count;
|
|
9518
|
-
}
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
// style for vertex edit mode
|
|
9522
|
-
function getVertexStyle(lyr, o) {
|
|
9523
|
-
return {
|
|
9524
|
-
ids: o.ids,
|
|
9525
|
-
overlay: true,
|
|
9526
|
-
strokeColor: black,
|
|
9527
|
-
strokeWidth: 1.5,
|
|
9528
|
-
vertices: true,
|
|
9529
|
-
vertex_overlay_color: violet,
|
|
9530
|
-
vertex_overlay: o.hit_coordinates || null,
|
|
9531
|
-
selected_points: o.selected_points || null,
|
|
9532
|
-
fillColor: null
|
|
9533
|
-
};
|
|
9534
|
-
}
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
function getSelectedFeatureStyle(lyr, o, opts) {
|
|
9538
|
-
var isPinned = o.pinned;
|
|
9539
|
-
var inSelection = o.ids.indexOf(o.id) > -1;
|
|
9540
|
-
var geomType = lyr.geometry_type;
|
|
9541
|
-
var style;
|
|
9542
|
-
if (isPinned && opts.interactionMode == 'rectangles') {
|
|
9543
|
-
// kludge for rectangle editing mode
|
|
9544
|
-
style = selectionStyles[geomType];
|
|
9545
|
-
} else if (isPinned) {
|
|
9546
|
-
// a feature is pinned
|
|
9547
|
-
style = pinnedStyles[geomType];
|
|
9548
|
-
} else if (inSelection) {
|
|
9549
|
-
// normal hover, or hover id is in the selection set
|
|
9550
|
-
style = hoverStyles[geomType];
|
|
9551
|
-
} else {
|
|
9552
|
-
// features are selected, but hover id is not in the selection set
|
|
9553
|
-
style = unselectedHoverStyles[geomType];
|
|
9554
|
-
}
|
|
9555
|
-
return getDefaultStyle(lyr, style);
|
|
9556
|
-
}
|
|
9557
|
-
|
|
9558
|
-
// Modify style to use scaled circle instead of dot symbol
|
|
9559
|
-
function getOverlayPointStyler(baseStyler, overlayStyler) {
|
|
9560
|
-
return function(obj, i) {
|
|
9561
|
-
var dotColor;
|
|
9562
|
-
var id = obj.ids ? obj.ids[i] : -1;
|
|
9563
|
-
obj.strokeWidth = 0; // kludge to support setting minimum stroke width
|
|
9564
|
-
baseStyler(obj, id);
|
|
9565
|
-
if (overlayStyler) {
|
|
9566
|
-
overlayStyler(obj, i);
|
|
9567
|
-
}
|
|
9568
|
-
dotColor = obj.dotColor;
|
|
9569
|
-
if (obj.radius && dotColor) {
|
|
9570
|
-
obj.radius += 0.4;
|
|
9571
|
-
// delete obj.fillColor; // only show outline
|
|
9572
|
-
obj.fillColor = dotColor; // comment out to only highlight stroke
|
|
9573
|
-
obj.strokeColor = dotColor;
|
|
9574
|
-
obj.strokeWidth = Math.max(obj.strokeWidth + 0.8, 1.5);
|
|
9575
|
-
obj.opacity = 1;
|
|
9576
|
-
}
|
|
9577
|
-
};
|
|
9578
|
-
}
|
|
9579
|
-
|
|
9580
|
-
function getCanvasDisplayStyle(lyr) {
|
|
9581
|
-
var styleIndex = {
|
|
9582
|
-
opacity: 'opacity',
|
|
9583
|
-
r: 'radius',
|
|
9584
|
-
'fill': 'fillColor',
|
|
9585
|
-
'fill-pattern': 'fillPattern',
|
|
9586
|
-
'fill-effect': 'fillEffect',
|
|
9587
|
-
'fill-opacity': 'fillOpacity',
|
|
9588
|
-
'stroke': 'strokeColor',
|
|
9589
|
-
'stroke-width': 'strokeWidth',
|
|
9590
|
-
'stroke-dasharray': 'lineDash',
|
|
9591
|
-
'stroke-opacity': 'strokeOpacity',
|
|
9592
|
-
'stroke-linecap': 'lineCap',
|
|
9593
|
-
'stroke-linejoin': 'lineJoin',
|
|
9594
|
-
'stroke-miterlimit': 'miterLimit'
|
|
9595
|
-
},
|
|
9596
|
-
// array of field names of relevant svg display properties
|
|
9597
|
-
fields = getCanvasStyleFields(lyr).filter(function(f) {return f in styleIndex;}),
|
|
9598
|
-
records = lyr.data.getRecords();
|
|
9599
|
-
var styler = function(style, i) {
|
|
9600
|
-
var rec = records[i];
|
|
9601
|
-
var fname, val;
|
|
9602
|
-
for (var j=0; j<fields.length; j++) {
|
|
9603
|
-
fname = fields[j];
|
|
9604
|
-
val = rec && rec[fname];
|
|
9605
|
-
if (val == 'none') {
|
|
9606
|
-
val = 'transparent'; // canvas equivalent of CSS 'none'
|
|
9607
|
-
}
|
|
9608
|
-
// convert svg property name to mapshaper style equivalent
|
|
9609
|
-
style[styleIndex[fname]] = val;
|
|
9610
|
-
}
|
|
9611
|
-
|
|
9612
|
-
if (style.strokeWidth && !style.strokeColor) {
|
|
9613
|
-
style.strokeColor = 'black';
|
|
9614
|
-
}
|
|
9615
|
-
if (!('strokeWidth' in style) && style.strokeColor) {
|
|
9616
|
-
style.strokeWidth = 1;
|
|
10013
|
+
function findPathStartInfo(e) {
|
|
10014
|
+
var target = hit.getHitTarget();
|
|
10015
|
+
var arcId = target.arcs.size() - 1;
|
|
10016
|
+
var data = target.arcs.getVertexData();
|
|
10017
|
+
var id = data.ii[arcId];
|
|
10018
|
+
var x = data.xx[id];
|
|
10019
|
+
var y = data.yy[id];
|
|
10020
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10021
|
+
var dist = geom.distance2D(p[0], p[1], x, y);
|
|
10022
|
+
var pathLen = data.nn[arcId];
|
|
10023
|
+
var pixelDist = dist / ext.getPixelSize();
|
|
10024
|
+
if (pixelDist > HOVER_THRESHOLD$1 || pathLen < 4) {
|
|
10025
|
+
return null;
|
|
9617
10026
|
}
|
|
9618
|
-
|
|
9619
|
-
|
|
10027
|
+
var point = translateDisplayPoint([x, y]);
|
|
10028
|
+
return {
|
|
10029
|
+
target, ids: [id], extendable: false, point
|
|
10030
|
+
};
|
|
10031
|
+
}
|
|
10032
|
+
|
|
10033
|
+
// return data on the nearest vertex (or identical vertices) to the pointer
|
|
10034
|
+
// (if within a distance threshold)
|
|
10035
|
+
//
|
|
10036
|
+
function findDraggableVertices(e) {
|
|
10037
|
+
var target = hit.getHitTarget();
|
|
10038
|
+
var shp = target.layer.shapes[e.id];
|
|
10039
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10040
|
+
var ids = internal.findNearestVertices(p, shp, target.arcs);
|
|
10041
|
+
var p2 = target.arcs.getVertex2(ids[0]);
|
|
10042
|
+
var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
|
|
10043
|
+
var pixelDist = dist / ext.getPixelSize();
|
|
10044
|
+
if (pixelDist > HOVER_THRESHOLD$1) {
|
|
10045
|
+
return null;
|
|
9620
10046
|
}
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
10047
|
+
var point = getVertexCoords(target, ids[0]); // data coordinates
|
|
10048
|
+
// find out if the vertex is the endpoint of a single path
|
|
10049
|
+
// (which could be extended by a newly drawn path)
|
|
10050
|
+
var extendable = ids.length == 1 &&
|
|
10051
|
+
internal.vertexIsArcEndpoint(ids[0], target.arcs);
|
|
10052
|
+
return {target, ids, extendable, point};
|
|
10053
|
+
}
|
|
9624
10054
|
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
10055
|
+
function findMidpointInsertionPoint(e) {
|
|
10056
|
+
var target = hit.getHitTarget();
|
|
10057
|
+
//// vertex insertion not supported with simplification
|
|
10058
|
+
// if (!target.arcs.isFlat()) return null;
|
|
10059
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10060
|
+
var midpoint = findNearestMidpoint$1(p, e.id, target);
|
|
10061
|
+
if (!midpoint ||
|
|
10062
|
+
midpoint.distance / ext.getPixelSize() > MIDPOINT_THRESHOLD$1) return null;
|
|
10063
|
+
return midpoint;
|
|
9630
10064
|
}
|
|
9631
|
-
return utils$1.difference(fields, ['opacity', 'class']).length > 0;
|
|
9632
10065
|
}
|
|
9633
10066
|
|
|
9634
10067
|
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
10068
|
+
// Given a location @p (e.g. corresponding to the mouse pointer location),
|
|
10069
|
+
// find the midpoint of two vertices on @shp suitable for inserting a new vertex
|
|
10070
|
+
function findNearestMidpoint$1(p, fid, target) {
|
|
10071
|
+
var arcs = target.arcs;
|
|
10072
|
+
var shp = target.layer.shapes[fid];
|
|
10073
|
+
var minDist = Infinity, v;
|
|
10074
|
+
internal.forEachSegmentInShape(shp, arcs, function(i, j, xx, yy) {
|
|
10075
|
+
var x1 = xx[i],
|
|
10076
|
+
y1 = yy[i],
|
|
10077
|
+
x2 = xx[j],
|
|
10078
|
+
y2 = yy[j],
|
|
10079
|
+
cx = (x1 + x2) / 2,
|
|
10080
|
+
cy = (y1 + y2) / 2,
|
|
10081
|
+
midpoint = [cx, cy],
|
|
10082
|
+
dist = geom.distance2D(cx, cy, p[0], p[1]);
|
|
10083
|
+
if (dist < minDist) {
|
|
10084
|
+
minDist = dist;
|
|
10085
|
+
v = {
|
|
10086
|
+
i: (i < j ? i : j) + 1, // insertion point
|
|
10087
|
+
segment: [i, j],
|
|
10088
|
+
segmentLen: geom.distance2D(x1, y1, x2, y2),
|
|
10089
|
+
displayPoint: midpoint,
|
|
10090
|
+
point: translateDisplayPoint(target, midpoint),
|
|
10091
|
+
distance: dist
|
|
10092
|
+
};
|
|
10093
|
+
}
|
|
10094
|
+
});
|
|
10095
|
+
return v || null;
|
|
9638
10096
|
}
|
|
9639
10097
|
|
|
9640
|
-
function initPointDrawing(gui, ext,
|
|
10098
|
+
function initPointDrawing(gui, ext, hit) {
|
|
10099
|
+
var mouse = gui.map.getMouse();
|
|
9641
10100
|
|
|
9642
10101
|
gui.on('interaction_mode_change', function(e) {
|
|
9643
10102
|
gui.container.findChild('.map-layers').classed('add-points', e.mode === 'add-points');
|
|
@@ -9659,114 +10118,201 @@
|
|
|
9659
10118
|
var target = hit.getHitTarget();
|
|
9660
10119
|
var lyr = target.layer;
|
|
9661
10120
|
var fid = lyr.shapes.length;
|
|
9662
|
-
var d =
|
|
10121
|
+
var d = appendNewDataRecord(lyr);
|
|
9663
10122
|
if (d) {
|
|
9664
10123
|
// this seems to work even for projected layers -- the data tables
|
|
9665
10124
|
// of projected and original data seem to be shared.
|
|
9666
10125
|
lyr.data.getRecords()[fid] = d;
|
|
9667
|
-
// TODO: handle SVG symbol layer
|
|
9668
|
-
if (internal.layerHasLabels(lyr)) {
|
|
9669
|
-
d['label-text'] = 'TBD'; // without text, new labels will be invisible
|
|
9670
|
-
} else if (layerHasCanvasDisplayStyle(lyr)) {
|
|
9671
|
-
d.r = 3; // show a black circle if layer is styled
|
|
9672
|
-
}
|
|
9673
10126
|
}
|
|
9674
10127
|
lyr.shapes[fid] = [p];
|
|
9675
10128
|
updatePointCoords(target, fid);
|
|
9676
10129
|
}
|
|
9677
10130
|
|
|
9678
|
-
|
|
9679
|
-
return table.getFields().reduce(function(memo, name) {
|
|
9680
|
-
memo[name] = null;
|
|
9681
|
-
return memo;
|
|
9682
|
-
}, {});
|
|
9683
|
-
}
|
|
10131
|
+
|
|
9684
10132
|
}
|
|
9685
10133
|
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
var _lastClick;
|
|
10134
|
+
// pointer thresholds for hovering near a vertex or segment midpoint
|
|
10135
|
+
var HOVER_THRESHOLD = 8;
|
|
10136
|
+
var MIDPOINT_THRESHOLD = 11;
|
|
9690
10137
|
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
});
|
|
10138
|
+
function initVertexDragging(gui, ext, hit) {
|
|
10139
|
+
var insertionPoint;
|
|
10140
|
+
var dragInfo;
|
|
9695
10141
|
|
|
9696
10142
|
function active() {
|
|
9697
|
-
return
|
|
10143
|
+
return gui.interaction.getMode() == 'vertices';
|
|
9698
10144
|
}
|
|
9699
10145
|
|
|
9700
|
-
function
|
|
9701
|
-
return active() && !!
|
|
10146
|
+
function dragging() {
|
|
10147
|
+
return active() && !!dragInfo;
|
|
9702
10148
|
}
|
|
9703
10149
|
|
|
9704
|
-
function
|
|
9705
|
-
|
|
10150
|
+
function setHoverVertex(id) {
|
|
10151
|
+
var target = hit.getHitTarget();
|
|
10152
|
+
hit.setHoverVertex(target.arcs.getVertex2(id));
|
|
9706
10153
|
}
|
|
9707
10154
|
|
|
9708
|
-
function
|
|
10155
|
+
function clearHoverVertex() {
|
|
10156
|
+
hit.clearHoverVertex();
|
|
10157
|
+
}
|
|
9709
10158
|
|
|
10159
|
+
// return data on the nearest vertex (or identical vertices) to the pointer
|
|
10160
|
+
// (if within a distance threshold)
|
|
10161
|
+
//
|
|
10162
|
+
function findDraggableVertices(e) {
|
|
10163
|
+
var target = hit.getHitTarget();
|
|
10164
|
+
var shp = target.layer.shapes[e.id];
|
|
10165
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10166
|
+
var nearestIds = internal.findNearestVertices(p, shp, target.arcs);
|
|
10167
|
+
var p2 = target.arcs.getVertex2(nearestIds[0]);
|
|
10168
|
+
var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
|
|
10169
|
+
var pixelDist = dist / ext.getPixelSize();
|
|
10170
|
+
if (pixelDist > HOVER_THRESHOLD) {
|
|
10171
|
+
return null;
|
|
10172
|
+
}
|
|
10173
|
+
var points = nearestIds.map(function(i) {
|
|
10174
|
+
return getVertexCoords(target, i); // data coordinates
|
|
10175
|
+
});
|
|
10176
|
+
return {
|
|
10177
|
+
target: target,
|
|
10178
|
+
ids: nearestIds,
|
|
10179
|
+
points: points
|
|
10180
|
+
};
|
|
9710
10181
|
}
|
|
9711
10182
|
|
|
9712
|
-
function
|
|
9713
|
-
|
|
10183
|
+
function findVertexInsertionPoint(e) {
|
|
10184
|
+
var target = hit.getHitTarget();
|
|
10185
|
+
//// vertex insertion not supported with simplification
|
|
10186
|
+
// if (!target.arcs.isFlat()) return null;
|
|
10187
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10188
|
+
var midpoint = findNearestMidpoint(p, e.id, target);
|
|
10189
|
+
if (!midpoint ||
|
|
10190
|
+
midpoint.distance / ext.getPixelSize() > MIDPOINT_THRESHOLD) return null;
|
|
10191
|
+
return midpoint;
|
|
9714
10192
|
}
|
|
9715
10193
|
|
|
9716
|
-
|
|
10194
|
+
hit.on('dragstart', function(e) {
|
|
9717
10195
|
if (!active()) return;
|
|
9718
|
-
if (
|
|
9719
|
-
|
|
10196
|
+
if (insertionPoint) {
|
|
10197
|
+
var target = hit.getHitTarget();
|
|
10198
|
+
insertVertex$1(target, insertionPoint.i, insertionPoint.point);
|
|
10199
|
+
dragInfo = {
|
|
10200
|
+
target: target,
|
|
10201
|
+
insertion: true,
|
|
10202
|
+
ids: [insertionPoint.i],
|
|
10203
|
+
points: [insertionPoint.point]
|
|
10204
|
+
};
|
|
10205
|
+
insertionPoint = null;
|
|
10206
|
+
} else {
|
|
10207
|
+
dragInfo = findDraggableVertices(e);
|
|
10208
|
+
}
|
|
10209
|
+
if (dragInfo) {
|
|
10210
|
+
setHoverVertex(dragInfo.ids[0]);
|
|
9720
10211
|
}
|
|
9721
10212
|
});
|
|
9722
10213
|
|
|
9723
|
-
|
|
9724
|
-
if (!
|
|
9725
|
-
|
|
9726
|
-
|
|
10214
|
+
hit.on('drag', function(e) {
|
|
10215
|
+
if (!dragging()) return;
|
|
10216
|
+
var target = hit.getHitTarget();
|
|
10217
|
+
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10218
|
+
if (gui.keyboard.shiftIsPressed()) {
|
|
10219
|
+
internal.snapPointToArcEndpoint(p, dragInfo.ids, target.arcs);
|
|
10220
|
+
}
|
|
10221
|
+
internal.snapVerticesToPoint(dragInfo.ids, p, target.arcs);
|
|
10222
|
+
setHoverVertex(dragInfo.ids[0]);
|
|
10223
|
+
// redrawing the whole map updates the data layer as well as the overlay layer
|
|
9727
10224
|
// gui.dispatchEvent('map-needs-refresh');
|
|
9728
|
-
_lastClick = e;
|
|
9729
10225
|
});
|
|
9730
10226
|
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
10227
|
+
hit.on('dragend', function(e) {
|
|
10228
|
+
if (!dragging()) return;
|
|
10229
|
+
// kludge to get dataset to recalculate internal bounding boxes
|
|
10230
|
+
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
10231
|
+
clearHoverVertex();
|
|
10232
|
+
updateVertexCoords(dragInfo.target, dragInfo.ids);
|
|
10233
|
+
gui.dispatchEvent('vertex_dragend', dragInfo);
|
|
10234
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
10235
|
+
dragInfo = null;
|
|
10236
|
+
});
|
|
9739
10237
|
|
|
9740
|
-
|
|
10238
|
+
hit.on('dblclick', function(e) {
|
|
9741
10239
|
if (!active()) return;
|
|
10240
|
+
var info = findDraggableVertices(e); // same selection criteria as for dragging
|
|
10241
|
+
if (!info) return;
|
|
10242
|
+
var target = hit.getHitTarget();
|
|
10243
|
+
var vId = info.ids[0];
|
|
10244
|
+
if (internal.vertexIsArcStart(vId, target.arcs) ||
|
|
10245
|
+
internal.vertexIsArcEnd(vId, target.arcs)) {
|
|
10246
|
+
// TODO: support removing arc endpoints
|
|
10247
|
+
return;
|
|
10248
|
+
}
|
|
10249
|
+
gui.dispatchEvent('vertex_delete', {
|
|
10250
|
+
target: target,
|
|
10251
|
+
vertex_id: vId
|
|
10252
|
+
});
|
|
10253
|
+
deleteVertex$1(target, vId);
|
|
10254
|
+
clearHoverVertex();
|
|
10255
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
9742
10256
|
});
|
|
9743
10257
|
|
|
9744
|
-
//
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
var
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
lyr.data.getRecords()[fid] = getEmptyDataRecord(lyr.data);
|
|
10258
|
+
// highlight hit vertex in path edit mode
|
|
10259
|
+
hit.on('hover', function(e) {
|
|
10260
|
+
insertionPoint = null;
|
|
10261
|
+
if (!active() || dragging()) return; // no hover effect while dragging
|
|
10262
|
+
var info = findDraggableVertices(e);
|
|
10263
|
+
if (info) {
|
|
10264
|
+
// hovering near a vertex: highlight the vertex
|
|
10265
|
+
setHoverVertex(info.ids[0]);
|
|
10266
|
+
return;
|
|
9754
10267
|
}
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
10268
|
+
// if hovering near a segment midpoint: show the midpoint and save midpoint info
|
|
10269
|
+
insertionPoint = findVertexInsertionPoint(e);
|
|
10270
|
+
if (insertionPoint) {
|
|
10271
|
+
hit.setHoverVertex(insertionPoint.displayPoint);
|
|
10272
|
+
} else {
|
|
10273
|
+
// pointer is not over a vertex: clear any hover effect
|
|
10274
|
+
clearHoverVertex();
|
|
10275
|
+
}
|
|
10276
|
+
}, null, 100);
|
|
10277
|
+
}
|
|
9758
10278
|
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
10279
|
+
|
|
10280
|
+
// Given a location @p (e.g. corresponding to the mouse pointer location),
|
|
10281
|
+
// find the midpoint of two vertices on @shp suitable for inserting a new vertex
|
|
10282
|
+
function findNearestMidpoint(p, fid, target) {
|
|
10283
|
+
var arcs = target.arcs;
|
|
10284
|
+
var shp = target.layer.shapes[fid];
|
|
10285
|
+
var minDist = Infinity, v;
|
|
10286
|
+
internal.forEachSegmentInShape(shp, arcs, function(i, j, xx, yy) {
|
|
10287
|
+
var x1 = xx[i],
|
|
10288
|
+
y1 = yy[i],
|
|
10289
|
+
x2 = xx[j],
|
|
10290
|
+
y2 = yy[j],
|
|
10291
|
+
cx = (x1 + x2) / 2,
|
|
10292
|
+
cy = (y1 + y2) / 2,
|
|
10293
|
+
midpoint = [cx, cy],
|
|
10294
|
+
dist = geom.distance2D(cx, cy, p[0], p[1]);
|
|
10295
|
+
if (dist < minDist) {
|
|
10296
|
+
minDist = dist;
|
|
10297
|
+
v = {
|
|
10298
|
+
i: (i < j ? i : j) + 1, // insertion point
|
|
10299
|
+
segment: [i, j],
|
|
10300
|
+
segmentLen: geom.distance2D(x1, y1, x2, y2),
|
|
10301
|
+
displayPoint: midpoint,
|
|
10302
|
+
point: translateDisplayPoint(target, midpoint),
|
|
10303
|
+
distance: dist
|
|
10304
|
+
};
|
|
10305
|
+
}
|
|
10306
|
+
});
|
|
10307
|
+
return v || null;
|
|
9765
10308
|
}
|
|
9766
10309
|
|
|
9767
|
-
function
|
|
9768
|
-
|
|
9769
|
-
|
|
10310
|
+
function initInteractiveEditing(gui, ext, hit) {
|
|
10311
|
+
initLabelDragging(gui, ext, hit);
|
|
10312
|
+
initPointDragging(gui, ext, hit);
|
|
10313
|
+
initPointDrawing(gui, ext, hit);
|
|
10314
|
+
initLineEditing(gui, ext, hit);
|
|
10315
|
+
initVertexDragging(gui, ext, hit);
|
|
9770
10316
|
}
|
|
9771
10317
|
|
|
9772
10318
|
function MapExtent(_position) {
|
|
@@ -10317,7 +10863,7 @@
|
|
|
10317
10863
|
_ctx.beginPath();
|
|
10318
10864
|
_ctx.fillStyle = style.vertex_overlay_color || 'black';
|
|
10319
10865
|
p = style.vertex_overlay;
|
|
10320
|
-
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius *
|
|
10866
|
+
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius * 2, _ctx);
|
|
10321
10867
|
_ctx.fill();
|
|
10322
10868
|
_ctx.closePath();
|
|
10323
10869
|
}
|
|
@@ -10964,6 +11510,10 @@
|
|
|
10964
11510
|
runCommand(cmd);
|
|
10965
11511
|
});
|
|
10966
11512
|
|
|
11513
|
+
new SimpleButton(popup.findChild('.frame-btn')).on('click', function() {
|
|
11514
|
+
openAddFramePopup(gui, box.getDataCoords());
|
|
11515
|
+
});
|
|
11516
|
+
|
|
10967
11517
|
gui.addMode('box_tool', turnOn, turnOff);
|
|
10968
11518
|
|
|
10969
11519
|
gui.on('interaction_mode_change', function(e) {
|
|
@@ -11036,6 +11586,30 @@
|
|
|
11036
11586
|
hideCoords();
|
|
11037
11587
|
}
|
|
11038
11588
|
|
|
11589
|
+
function openAddFramePopup(gui, bbox) {
|
|
11590
|
+
var popup = showPopupAlert('', 'Add a map frame');
|
|
11591
|
+
var el = popup.container();
|
|
11592
|
+
el.addClass('option-menu');
|
|
11593
|
+
var html = `<p>Enter a width in px, cm or inches to create a frame layer
|
|
11594
|
+
for setting the size of the map for symbol scaling in the
|
|
11595
|
+
GUI and setting the size and crop of SVG output.</p><div><input type="text" class="frame-width text-input" placeholder="examples: 600px 5in"></div>
|
|
11596
|
+
<div tabindex="0" class="btn dialog-btn">Create</div></span>`;
|
|
11597
|
+
el.html(html);
|
|
11598
|
+
var input = el.findChild('.frame-width');
|
|
11599
|
+
input.node().focus();
|
|
11600
|
+
var btn = el.findChild('.btn').on('click', function() {
|
|
11601
|
+
var widthStr = input.node().value.trim();
|
|
11602
|
+
if (parseFloat(widthStr) > 0 === false) {
|
|
11603
|
+
// invalid input
|
|
11604
|
+
input.node().value = '';
|
|
11605
|
+
return;
|
|
11606
|
+
}
|
|
11607
|
+
var cmd = `-rectangle + name=frame bbox='${bbox.join(',')}' width='${widthStr}'`;
|
|
11608
|
+
runCommand(cmd);
|
|
11609
|
+
popup.close();
|
|
11610
|
+
});
|
|
11611
|
+
}
|
|
11612
|
+
|
|
11039
11613
|
return self;
|
|
11040
11614
|
}
|
|
11041
11615
|
|
|
@@ -11143,6 +11717,7 @@
|
|
|
11143
11717
|
return filteredArcs;
|
|
11144
11718
|
}
|
|
11145
11719
|
|
|
11720
|
+
// TODO: better job of detecting arc change... e.g. revision number
|
|
11146
11721
|
unfilteredArcs.getScaledArcs = function(ext) {
|
|
11147
11722
|
if (filteredArcs) {
|
|
11148
11723
|
// match simplification of unfiltered arcs
|
|
@@ -11646,7 +12221,6 @@
|
|
|
11646
12221
|
model.on('update', onUpdate);
|
|
11647
12222
|
|
|
11648
12223
|
|
|
11649
|
-
|
|
11650
12224
|
// Update display of segment intersections
|
|
11651
12225
|
this.setIntersectionLayer = function(lyr, dataset) {
|
|
11652
12226
|
if (lyr == _intersectionLyr) return; // no change
|
|
@@ -11692,6 +12266,14 @@
|
|
|
11692
12266
|
this.isActiveLayer = isActiveLayer;
|
|
11693
12267
|
this.isVisibleLayer = isVisibleLayer;
|
|
11694
12268
|
this.getActiveLayer = function() { return _activeLyr; };
|
|
12269
|
+
// this.getViewData = function() {
|
|
12270
|
+
// return {
|
|
12271
|
+
// isPreview: isPreviewView(),
|
|
12272
|
+
// isTable: isTableView(),
|
|
12273
|
+
// isEmpty: !_activeLyr,
|
|
12274
|
+
// dynamicCRS: _dynamicCRS || null
|
|
12275
|
+
// };
|
|
12276
|
+
// };
|
|
11695
12277
|
|
|
11696
12278
|
// called by layer menu after layer visibility is updated
|
|
11697
12279
|
this.redraw = function() {
|
|
@@ -11808,7 +12390,6 @@
|
|
|
11808
12390
|
new BoxTool(gui, _ext, _nav),
|
|
11809
12391
|
new RectangleControl(gui, _hit),
|
|
11810
12392
|
initInteractiveEditing(gui, _ext, _hit);
|
|
11811
|
-
initDrawing(gui, _ext, _mouse, _hit);
|
|
11812
12393
|
_hit.on('change', updateOverlayLayer);
|
|
11813
12394
|
}
|
|
11814
12395
|
|
|
@@ -12014,7 +12595,7 @@
|
|
|
12014
12595
|
// 'hover' highlight has changed -- only refresh overlay
|
|
12015
12596
|
// (default) anything could have changed
|
|
12016
12597
|
function drawLayers2(action) {
|
|
12017
|
-
// sometimes styles need to be regenerated with 'hover' action (when)
|
|
12598
|
+
// sometimes styles need to be regenerated with 'hover' action (when?)
|
|
12018
12599
|
var layersMayHaveChanged = action != 'nav'; // !action;
|
|
12019
12600
|
var fullBounds;
|
|
12020
12601
|
var contentLayers = getDrawableContentLayers();
|