]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index/history-changesets-layer.js
Add shadows to changeset highlights
[rails.git] / app / assets / javascripts / index / history-changesets-layer.js
1 OSM.HistoryChangesetBboxLayer = L.FeatureGroup.extend({
2   getLayerId: function (layer) {
3     return layer.id;
4   },
5
6   addChangesetLayer: function (changeset) {
7     const style = this._getChangesetStyle(changeset);
8     const rectangle = L.rectangle(changeset.bounds, style);
9     rectangle.id = changeset.id;
10     return this.addLayer(rectangle);
11   },
12
13   updateChangesetLayerBounds: function (changeset) {
14     this.getLayer(changeset.id)?.setBounds(changeset.bounds);
15   },
16
17   _getSidebarRelativeClassName: function ({ sidebarRelativePosition }) {
18     if (sidebarRelativePosition > 0) {
19       return "changeset-above-sidebar-viewport";
20     } else if (sidebarRelativePosition < 0) {
21       return "changeset-below-sidebar-viewport";
22     } else {
23       return "changeset-in-sidebar-viewport";
24     }
25   }
26 });
27
28 OSM.HistoryChangesetBboxAreaLayer = OSM.HistoryChangesetBboxLayer.extend({
29   _getChangesetStyle: function (changeset) {
30     return {
31       weight: 0,
32       fillOpacity: 0,
33       className: this._getSidebarRelativeClassName(changeset)
34     };
35   }
36 });
37
38 OSM.HistoryChangesetBboxOutlineLayer = OSM.HistoryChangesetBboxLayer.extend({
39   _getChangesetStyle: function (changeset) {
40     return {
41       weight: 4,
42       color: "var(--changeset-outline-color)",
43       fill: false,
44       className: this._getSidebarRelativeClassName(changeset)
45     };
46   }
47 });
48
49 OSM.HistoryChangesetBboxBorderLayer = OSM.HistoryChangesetBboxLayer.extend({
50   _getChangesetStyle: function (changeset) {
51     return {
52       weight: 2,
53       color: "var(--changeset-border-color)",
54       fill: false,
55       className: this._getSidebarRelativeClassName(changeset)
56     };
57   }
58 });
59
60 OSM.HistoryChangesetBboxHighlightAreaLayer = OSM.HistoryChangesetBboxLayer.extend({
61   _getChangesetStyle: function (changeset) {
62     return {
63       interactive: false,
64       stroke: false,
65       fillColor: "var(--changeset-fill-color)",
66       fillOpacity: 0.3,
67       className: this._getSidebarRelativeClassName(changeset)
68     };
69   }
70 });
71
72 OSM.HistoryChangesetBboxHighlightOutlineLayer = OSM.HistoryChangesetBboxLayer.extend({
73   _getChangesetStyle: function (changeset) {
74     return {
75       interactive: false,
76       weight: changeset.sidebarRelativePosition === 0 ? 8 : 6,
77       color: "var(--changeset-outline-color)",
78       fill: false,
79       className: this._getSidebarRelativeClassName(changeset) + " changeset-highlight-outline"
80     };
81   }
82 });
83
84 OSM.HistoryChangesetBboxHighlightBorderLayer = OSM.HistoryChangesetBboxLayer.extend({
85   _getChangesetStyle: function (changeset) {
86     return {
87       interactive: false,
88       weight: 4,
89       color: "var(--changeset-border-color)",
90       fill: false,
91       className: this._getSidebarRelativeClassName(changeset)
92     };
93   }
94 });
95
96 OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
97   updateChangesets: function (map, changesets) {
98     this._changesets = new Map(changesets.map(changeset => [changeset.id, changeset]));
99     this.updateChangesetsGeometry(map);
100   },
101
102   updateChangesetsGeometry: function (map) {
103     const changesetSizeLowerBound = 20, // Min width/height of changeset in pixels
104           mapViewExpansion = 2; // Half of bbox border+outline width in pixels
105
106     const mapViewCenterLng = map.getCenter().lng,
107           mapViewPixelBounds = map.getPixelBounds();
108
109     mapViewPixelBounds.min.x -= mapViewExpansion;
110     mapViewPixelBounds.min.y -= mapViewExpansion;
111     mapViewPixelBounds.max.x += mapViewExpansion;
112     mapViewPixelBounds.max.y += mapViewExpansion;
113
114     for (const changeset of this._changesets.values()) {
115       const changesetNorthWestLatLng = L.latLng(changeset.bbox.maxlat, changeset.bbox.minlon),
116             changesetSouthEastLatLng = L.latLng(changeset.bbox.minlat, changeset.bbox.maxlon),
117             changesetCenterLng = (changesetNorthWestLatLng.lng + changesetSouthEastLatLng.lng) / 2,
118             shiftInWorldCircumferences = Math.round((changesetCenterLng - mapViewCenterLng) / 360);
119
120       if (shiftInWorldCircumferences) {
121         changesetNorthWestLatLng.lng -= shiftInWorldCircumferences * 360;
122         changesetSouthEastLatLng.lng -= shiftInWorldCircumferences * 360;
123       }
124
125       const changesetMinCorner = map.project(changesetNorthWestLatLng),
126             changesetMaxCorner = map.project(changesetSouthEastLatLng),
127             changesetSizeX = changesetMaxCorner.x - changesetMinCorner.x,
128             changesetSizeY = changesetMaxCorner.y - changesetMinCorner.y;
129
130       if (changesetSizeX < changesetSizeLowerBound) {
131         changesetMinCorner.x -= (changesetSizeLowerBound - changesetSizeX) / 2;
132         changesetMaxCorner.x += (changesetSizeLowerBound - changesetSizeX) / 2;
133       }
134
135       if (changesetSizeY < changesetSizeLowerBound) {
136         changesetMinCorner.y -= (changesetSizeLowerBound - changesetSizeY) / 2;
137         changesetMaxCorner.y += (changesetSizeLowerBound - changesetSizeY) / 2;
138       }
139
140       changeset.bounds = L.latLngBounds(map.unproject(changesetMinCorner),
141                                         map.unproject(changesetMaxCorner));
142
143       const changesetPixelBounds = L.bounds(changesetMinCorner, changesetMaxCorner);
144
145       changeset.hasEdgesInMapView = changesetPixelBounds.overlaps(mapViewPixelBounds) &&
146                                     !changesetPixelBounds.contains(mapViewPixelBounds);
147     }
148
149     this.updateChangesetsOrder();
150   },
151
152   updateChangesetsOrder: function () {
153     const changesetEntries = [...this._changesets];
154     changesetEntries.sort(([, a], [, b]) => b.bounds.getSize() - a.bounds.getSize());
155     this._changesets = new Map(changesetEntries);
156
157     for (const layer of this._bboxLayers) {
158       layer.clearLayers();
159     }
160
161     for (const changeset of this._changesets.values()) {
162       if (changeset.sidebarRelativePosition !== 0 && changeset.hasEdgesInMapView) {
163         this._areaLayer.addChangesetLayer(changeset);
164       }
165     }
166
167     for (const changeset of this._changesets.values()) {
168       if (changeset.sidebarRelativePosition === 0 && changeset.hasEdgesInMapView) {
169         this._areaLayer.addChangesetLayer(changeset);
170       }
171     }
172
173     for (const changeset of this._changesets.values()) {
174       if (changeset.sidebarRelativePosition !== 0) {
175         this._borderLayer.addChangesetLayer(changeset);
176       }
177     }
178
179     for (const changeset of this._changesets.values()) {
180       if (changeset.sidebarRelativePosition === 0) {
181         this._outlineLayer.addChangesetLayer(changeset);
182       }
183     }
184
185     for (const changeset of this._changesets.values()) {
186       if (changeset.sidebarRelativePosition === 0) {
187         this._borderLayer.addChangesetLayer(changeset);
188       }
189     }
190   },
191
192   toggleChangesetHighlight: function (id, state) {
193     const changeset = this._changesets.get(id);
194     if (!changeset) return;
195
196     if (state) {
197       this._highlightAreaLayer.addChangesetLayer(changeset);
198       this._highlightOutlineLayer.addChangesetLayer(changeset);
199       this._highlightBorderLayer.addChangesetLayer(changeset);
200     } else {
201       this._highlightAreaLayer.removeLayer(id);
202       this._highlightOutlineLayer.removeLayer(id);
203       this._highlightBorderLayer.removeLayer(id);
204     }
205   },
206
207   setChangesetSidebarRelativePosition: function (id, state) {
208     const changeset = this._changesets.get(id);
209     if (!changeset) return;
210     changeset.sidebarRelativePosition = state;
211   }
212 });
213
214 OSM.HistoryChangesetsLayer.addInitHook(function () {
215   this._changesets = new Map;
216
217   this._bboxLayers = [
218     this._areaLayer = new OSM.HistoryChangesetBboxAreaLayer().addTo(this),
219     this._outlineLayer = new OSM.HistoryChangesetBboxOutlineLayer().addTo(this),
220     this._borderLayer = new OSM.HistoryChangesetBboxBorderLayer().addTo(this),
221     this._highlightAreaLayer = new OSM.HistoryChangesetBboxHighlightAreaLayer().addTo(this),
222     this._highlightOutlineLayer = new OSM.HistoryChangesetBboxHighlightOutlineLayer().addTo(this),
223     this._highlightBorderLayer = new OSM.HistoryChangesetBboxHighlightBorderLayer().addTo(this)
224   ];
225 });