]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/index/export.js
Map XML download with Turbo
[rails.git] / app / assets / javascripts / index / export.js
1 OSM.Export = function (map) {
2   const page = {};
3
4   const locationFilter = new L.LocationFilter({
5     enableButton: false,
6     adjustButton: false
7   }).on("change", update);
8
9   function getBounds() {
10     return L.latLngBounds(
11       L.latLng($("#minlat").val(), $("#minlon").val()),
12       L.latLng($("#maxlat").val(), $("#maxlon").val()));
13   }
14
15   function boundsChanged() {
16     const bounds = getBounds();
17     map.fitBounds(bounds);
18     locationFilter.setBounds(bounds);
19     locationFilter.enable();
20     validateControls();
21   }
22
23   function enableFilter(e) {
24     e.preventDefault();
25
26     $("#drag_box").hide();
27
28     locationFilter.setBounds(map.getBounds().pad(-0.2));
29     locationFilter.enable();
30     validateControls();
31   }
32
33   function update() {
34     setBounds(locationFilter.isEnabled() ? locationFilter.getBounds() : map.getBounds());
35     validateControls();
36   }
37
38   function setBounds(bounds) {
39     const truncated = [bounds.getSouthWest(), bounds.getNorthEast()]
40       .map(c => OSM.cropLocation(c, map.getZoom()));
41     $("#minlon").val(truncated[0][1]);
42     $("#minlat").val(truncated[0][0]);
43     $("#maxlon").val(truncated[1][1]);
44     $("#maxlat").val(truncated[1][0]);
45
46     $("#export_overpass").attr("href",
47                                "https://overpass-api.de/api/map?bbox=" +
48                                truncated.map(p => p.reverse()).join());
49   }
50
51   function validateControls() {
52     $("#export_osm_too_large").toggle(getBounds().getSize() > OSM.MAX_REQUEST_AREA);
53     $("#export_commit").toggle(getBounds().getSize() < OSM.MAX_REQUEST_AREA);
54   }
55
56   function checkSubmit(e) {
57     if (getBounds().getSize() > OSM.MAX_REQUEST_AREA) e.preventDefault();
58   }
59
60   page.pushstate = page.popstate = function (path) {
61     OSM.loadSidebarContent(path, page.load);
62   };
63
64   page.load = function () {
65     map
66       .addLayer(locationFilter)
67       .on("moveend", update);
68
69     $("#maxlat, #minlon, #maxlon, #minlat").change(boundsChanged);
70     $("#drag_box").click(enableFilter);
71     $(".export_form").on("submit", checkSubmit);
72
73     function downloadBlob(blob, filename) {
74       const url = URL.createObjectURL(blob);
75       const a = document.createElement("a");
76       a.href = url;
77       a.download = filename;
78       document.body.appendChild(a);
79       a.click();
80       document.body.removeChild(a);
81       URL.revokeObjectURL(url);
82     }
83
84     async function handleExportSuccess(fetchResponse) {
85       try {
86         const blob = await fetchResponse.response.blob();
87         downloadBlob(blob, "map.osm");
88       } catch (err) {
89         // eslint-disable-next-line no-alert
90         alert(OSM.i18n.t("javascripts.share.export_failed", { reason: "(blob error)" }));
91       }
92     }
93
94     async function handleExportError(event) {
95       let detailMessage;
96       try {
97         detailMessage = event?.detail?.error?.message;
98         if (!detailMessage) {
99           const responseText = await event.detail.fetchResponse.responseText;
100           const parser = new DOMParser();
101           const doc = parser.parseFromString(responseText, "text/html");
102           detailMessage = doc.body ? doc.body.textContent.trim() : "(unknown)";
103         }
104       } catch (err) {
105         detailMessage = "(unknown)";
106       }
107       // eslint-disable-next-line no-alert
108       alert(OSM.i18n.t("javascripts.share.export_failed", { reason: detailMessage }));
109     }
110
111     document.querySelector(".export_form").addEventListener("turbo:submit-end", function (event) {
112       if (event.detail.success) {
113         handleExportSuccess(event.detail.fetchResponse);
114       } else {
115         handleExportError(event);
116       }
117     });
118
119     document.querySelector(".export_form").addEventListener("turbo:before-fetch-request", function (event) {
120       event.detail.fetchOptions.headers.Accept = "application/xml";
121     });
122
123     update();
124     return map.getState();
125   };
126
127   page.unload = function () {
128     map
129       .removeLayer(locationFilter)
130       .off("moveend", update);
131   };
132
133   return page;
134 };