1 OSM.HistoryChangesetBboxLayer = L.FeatureGroup.extend({
2 getLayerId: function (layer) {
6 addChangesetLayer: function (changeset) {
7 const style = this._getChangesetStyle(changeset);
8 const anchor = $(L.SVG.create("a")).attr("href", $(`#changeset_${changeset.id} a.changeset_id`).attr("href"));
9 const rectangle = L.rectangle(changeset.bounds, {
13 text: OSM.i18n.t("javascripts.context.scroll_to_changeset"),
15 this.fire("requestscrolltochangeset", { id: changeset.id }, true);
23 rectangle.id = changeset.id;
24 rectangle.on("add", function () {
25 $(this.getElement()).replaceWith(anchor).appendTo(anchor);
27 rectangle.on("remove", function () {
30 return this.addLayer(rectangle);
33 updateChangesetLayerBounds: function (changeset) {
34 this.getLayer(changeset.id)?.setBounds(changeset.bounds);
37 _getSidebarRelativeClassName: function ({ sidebarRelativePosition }) {
38 if (sidebarRelativePosition > 0) {
39 return "changeset-above-sidebar-viewport";
40 } else if (sidebarRelativePosition < 0) {
41 return "changeset-below-sidebar-viewport";
43 return "changeset-in-sidebar-viewport";
48 OSM.HistoryChangesetBboxAreaLayer = OSM.HistoryChangesetBboxLayer.extend({
49 _getChangesetStyle: function (changeset) {
53 className: this._getSidebarRelativeClassName(changeset)
58 OSM.HistoryChangesetBboxOutlineLayer = OSM.HistoryChangesetBboxLayer.extend({
59 _getChangesetStyle: function (changeset) {
62 color: "var(--changeset-outline-color)",
64 className: this._getSidebarRelativeClassName(changeset)
69 OSM.HistoryChangesetBboxBorderLayer = OSM.HistoryChangesetBboxLayer.extend({
70 _getChangesetStyle: function (changeset) {
73 color: "var(--changeset-border-color)",
75 className: this._getSidebarRelativeClassName(changeset)
80 OSM.HistoryChangesetBboxHighlightAreaLayer = OSM.HistoryChangesetBboxLayer.extend({
81 _getChangesetStyle: function (changeset) {
85 fillColor: "var(--changeset-fill-color)",
87 className: this._getSidebarRelativeClassName(changeset)
92 OSM.HistoryChangesetBboxHighlightOutlineLayer = OSM.HistoryChangesetBboxLayer.extend({
93 _getChangesetStyle: function (changeset) {
96 weight: changeset.sidebarRelativePosition === 0 ? 8 : 6,
97 color: "var(--changeset-outline-color)",
99 className: this._getSidebarRelativeClassName(changeset) + " changeset-highlight-outline"
104 OSM.HistoryChangesetBboxHighlightBorderLayer = OSM.HistoryChangesetBboxLayer.extend({
105 _getChangesetStyle: function (changeset) {
109 color: "var(--changeset-border-color)",
111 className: this._getSidebarRelativeClassName(changeset)
116 OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
117 updateChangesets: function (map, changesets) {
118 this._changesets = new Map(changesets.map(changeset => [changeset.id, changeset]));
119 this.updateChangesetsGeometry(map);
122 updateChangesetsGeometry: function (map) {
123 const changesetSizeLowerBound = 20, // Min width/height of changeset in pixels
124 mapViewExpansion = 2; // Half of bbox border+outline width in pixels
126 const mapViewCenterLng = map.getCenter().lng,
127 mapViewPixelBounds = map.getPixelBounds();
129 mapViewPixelBounds.min.x -= mapViewExpansion;
130 mapViewPixelBounds.min.y -= mapViewExpansion;
131 mapViewPixelBounds.max.x += mapViewExpansion;
132 mapViewPixelBounds.max.y += mapViewExpansion;
134 for (const changeset of this._changesets.values()) {
135 const changesetNorthWestLatLng = L.latLng(changeset.bbox.maxlat, changeset.bbox.minlon),
136 changesetSouthEastLatLng = L.latLng(changeset.bbox.minlat, changeset.bbox.maxlon),
137 changesetCenterLng = (changesetNorthWestLatLng.lng + changesetSouthEastLatLng.lng) / 2,
138 shiftInWorldCircumferences = Math.round((changesetCenterLng - mapViewCenterLng) / 360);
140 if (shiftInWorldCircumferences) {
141 changesetNorthWestLatLng.lng -= shiftInWorldCircumferences * 360;
142 changesetSouthEastLatLng.lng -= shiftInWorldCircumferences * 360;
145 const changesetMinCorner = map.project(changesetNorthWestLatLng),
146 changesetMaxCorner = map.project(changesetSouthEastLatLng),
147 changesetSizeX = changesetMaxCorner.x - changesetMinCorner.x,
148 changesetSizeY = changesetMaxCorner.y - changesetMinCorner.y;
150 if (changesetSizeX < changesetSizeLowerBound) {
151 changesetMinCorner.x -= (changesetSizeLowerBound - changesetSizeX) / 2;
152 changesetMaxCorner.x += (changesetSizeLowerBound - changesetSizeX) / 2;
155 if (changesetSizeY < changesetSizeLowerBound) {
156 changesetMinCorner.y -= (changesetSizeLowerBound - changesetSizeY) / 2;
157 changesetMaxCorner.y += (changesetSizeLowerBound - changesetSizeY) / 2;
160 changeset.bounds = L.latLngBounds(map.unproject(changesetMinCorner),
161 map.unproject(changesetMaxCorner));
163 const changesetPixelBounds = L.bounds(changesetMinCorner, changesetMaxCorner);
165 changeset.hasEdgesInMapView = changesetPixelBounds.overlaps(mapViewPixelBounds) &&
166 !changesetPixelBounds.contains(mapViewPixelBounds);
169 this.updateChangesetsOrder();
172 updateChangesetsOrder: function () {
173 const changesetEntries = [...this._changesets];
174 changesetEntries.sort(([, a], [, b]) => b.bounds.getSize() - a.bounds.getSize());
175 this._changesets = new Map(changesetEntries);
177 for (const layer of this._bboxLayers) {
181 for (const changeset of this._changesets.values()) {
182 if (changeset.sidebarRelativePosition !== 0 && changeset.hasEdgesInMapView) {
183 this._areaLayer.addChangesetLayer(changeset);
187 for (const changeset of this._changesets.values()) {
188 if (changeset.sidebarRelativePosition === 0 && changeset.hasEdgesInMapView) {
189 this._areaLayer.addChangesetLayer(changeset);
193 for (const changeset of this._changesets.values()) {
194 if (changeset.sidebarRelativePosition !== 0) {
195 this._borderLayer.addChangesetLayer(changeset);
199 for (const changeset of this._changesets.values()) {
200 if (changeset.sidebarRelativePosition === 0) {
201 this._outlineLayer.addChangesetLayer(changeset);
205 for (const changeset of this._changesets.values()) {
206 if (changeset.sidebarRelativePosition === 0) {
207 this._borderLayer.addChangesetLayer(changeset);
212 toggleChangesetHighlight: function (id, state) {
213 const changeset = this._changesets.get(id);
214 if (!changeset) return;
216 this._highlightAreaLayer.clearLayers();
217 this._highlightOutlineLayer.clearLayers();
218 this._highlightBorderLayer.clearLayers();
221 this._highlightAreaLayer.addChangesetLayer(changeset);
222 this._highlightOutlineLayer.addChangesetLayer(changeset);
223 this._highlightBorderLayer.addChangesetLayer(changeset);
227 setChangesetSidebarRelativePosition: function (id, state) {
228 const changeset = this._changesets.get(id);
229 if (!changeset) return;
230 changeset.sidebarRelativePosition = state;
234 OSM.HistoryChangesetsLayer.addInitHook(function () {
235 this._changesets = new Map;
238 this._areaLayer = new OSM.HistoryChangesetBboxAreaLayer().addTo(this),
239 this._outlineLayer = new OSM.HistoryChangesetBboxOutlineLayer().addTo(this),
240 this._borderLayer = new OSM.HistoryChangesetBboxBorderLayer().addTo(this),
241 this._highlightAreaLayer = new OSM.HistoryChangesetBboxHighlightAreaLayer().addTo(this),
242 this._highlightOutlineLayer = new OSM.HistoryChangesetBboxHighlightOutlineLayer().addTo(this),
243 this._highlightBorderLayer = new OSM.HistoryChangesetBboxHighlightBorderLayer().addTo(this)