]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index/history-changesets-layer.js
40732b9bf3f747b09cdaa0e7ca3908b13155c887
[rails.git] / app / assets / javascripts / index / history-changesets-layer.js
1 OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
2   _getSidebarRelativeClassName: function ({ sidebarRelativePosition }) {
3     if (sidebarRelativePosition > 0) {
4       return "changeset-above-sidebar-viewport";
5     } else if (sidebarRelativePosition < 0) {
6       return "changeset-below-sidebar-viewport";
7     } else {
8       return "changeset-in-sidebar-viewport";
9     }
10   },
11
12   _getInteractiveStyle: function (changeset) {
13     return {
14       weight: changeset.isHighlighted ? 4 : 2,
15       color: "var(--changeset-border-color)",
16       fillColor: "var(--changeset-fill-color)",
17       fillOpacity: changeset.isHighlighted ? 0.3 : 0,
18       className: this._getSidebarRelativeClassName(changeset) + (changeset.isHighlighted ? " changeset-highlighted" : "")
19     };
20   },
21
22   _updateChangesetStyle: function (changeset) {
23     const rect = this._interactiveLayer.getLayer(changeset.id);
24     if (!rect) return;
25
26     const style = this._getInteractiveStyle(changeset);
27     rect.setStyle(style);
28     // setStyle doesn't update css classes: https://github.com/leaflet/leaflet/issues/2662
29     rect._path.classList.value = style.className;
30     rect._path.classList.add("leaflet-interactive");
31   },
32
33   updateChangesets: function (map, changesets) {
34     this._changesets = new Map(changesets.map(changeset => [changeset.id, changeset]));
35     this.updateChangesetShapes(map);
36   },
37
38   updateChangesetShapes: function (map) {
39     for (const changeset of this._changesets.values()) {
40       const bottomLeft = map.project(L.latLng(changeset.bbox.minlat, changeset.bbox.minlon)),
41             topRight = map.project(L.latLng(changeset.bbox.maxlat, changeset.bbox.maxlon)),
42             width = topRight.x - bottomLeft.x,
43             height = bottomLeft.y - topRight.y,
44             minSize = 20; // Min width/height of changeset in pixels
45
46       if (width < minSize) {
47         bottomLeft.x -= ((minSize - width) / 2);
48         topRight.x += ((minSize - width) / 2);
49       }
50
51       if (height < minSize) {
52         bottomLeft.y += ((minSize - height) / 2);
53         topRight.y -= ((minSize - height) / 2);
54       }
55
56       changeset.bounds = L.latLngBounds(map.unproject(bottomLeft),
57                                         map.unproject(topRight));
58     }
59
60     this.updateChangesetLocations(map);
61     this.reorderChangesets();
62   },
63
64   updateChangesetLocations: function (map) {
65     const mapCenterLng = map.getCenter().lng;
66
67     for (const changeset of this._changesets.values()) {
68       const changesetSouthWest = changeset.bounds.getSouthWest();
69       const changesetNorthEast = changeset.bounds.getNorthEast();
70       const changesetCenterLng = (changesetSouthWest.lng + changesetNorthEast.lng) / 2;
71       const shiftInWorldCircumferences = Math.round((changesetCenterLng - mapCenterLng) / 360);
72
73       if (shiftInWorldCircumferences) {
74         changesetSouthWest.lng -= shiftInWorldCircumferences * 360;
75         changesetNorthEast.lng -= shiftInWorldCircumferences * 360;
76
77         this._interactiveLayer.getLayer(changeset.id)?.setBounds(changeset.bounds);
78       }
79     }
80   },
81
82   reorderChangesets: function () {
83     const changesetEntries = [...this._changesets];
84     changesetEntries.sort(([, a], [, b]) => {
85       const aInViewport = !a.sidebarRelativePosition;
86       const bInViewport = !b.sidebarRelativePosition;
87       if (aInViewport !== bInViewport) return aInViewport - bInViewport;
88       return b.bounds.getSize() - a.bounds.getSize();
89     });
90     this._changesets = new Map(changesetEntries);
91
92     this._interactiveLayer.clearLayers();
93
94     for (const changeset of this._changesets.values()) {
95       delete changeset.isHighlighted;
96       const rect = L.rectangle(changeset.bounds, this._getInteractiveStyle(changeset));
97       rect.id = changeset.id;
98       rect.addTo(this._interactiveLayer);
99     }
100   },
101
102   toggleChangesetHighlight: function (id, state) {
103     const changeset = this._changesets.get(id);
104     if (!changeset) return;
105
106     changeset.isHighlighted = state;
107     this._updateChangesetStyle(changeset);
108   },
109
110   setChangesetSidebarRelativePosition: function (id, state) {
111     const changeset = this._changesets.get(id);
112     if (!changeset) return;
113     changeset.sidebarRelativePosition = state;
114   }
115 });
116
117 OSM.HistoryChangesetsLayer.addInitHook(function () {
118   this._changesets = new Map;
119
120   this._interactiveLayer = L.featureGroup();
121   this._interactiveLayer.getLayerId = (layer) => layer.id;
122   this.addLayer(this._interactiveLayer);
123 });