e457e293ae4cc6ca8eebb8c471e9a78e925f2fac
[philipp/winterrodeln/mediawiki_extensions/wrmap.git] / wrmap.js
1 "use strict";
2
3 function init_wrmap(i, jq_map) {
4         // define constants
5         let EPSG4326 = ol.proj.get("EPSG:4326"); // lon/lat
6         let EPSG3857 = ol.proj.get("EPSG:3857"); // google
7
8         // tool functions
9         function createElement(tagName, attributes) {
10                 let element = $(document.createElement(tagName));
11                 if (attributes === undefined) return element;
12                 for (let attribute in attributes) {
13                         element.attr(attribute, attributes[attribute]);
14                 }
15                 return element;
16         }
17
18         function appendElement(parentElement, tagName, attributes) {
19                 if (attributes === undefined) attributes = {};
20                 let element = createElement(tagName, attributes);
21                 parentElement.append(element);
22                 return element;
23         }
24
25
26         // extract geojson from map element and clear map element's content
27         jq_map = $(jq_map);
28         let ext_path = jq_map.attr('data-ext-path'); // e.g. '/mediawiki/extensions/wrmap'
29         let img_path = ext_path + '/img';
30         let json_string = jq_map.children().last().text();
31         jq_map.empty(); // once parsed, remove geojson string from the map element.
32         let json_js = JSON.parse(json_string);
33         let format_geojson = new ol.format.GeoJSON();
34         let features_all = format_geojson.readFeatures(json_js, {dataProjection: EPSG4326, featureProjection: EPSG3857});
35
36
37         // path layer
38         // ----------
39
40         function get_feature_title(feature) {
41                 let title = feature.get('type');
42                 if (title == 'sledrun') return feature.get('name');
43                 title = title.charAt(0).toUpperCase() + title.slice(1); // first letter uppercase
44                 if (feature.get('name')) title += ': ' + feature.get('name');
45                 return title;
46         }
47
48         // Returns 0 to 5 for features that represent sledruns as their condition
49         let get_sledrun_condition = function(feature) {
50                 let condition = feature.get('condition');
51                 if (condition === undefined) return 0;
52                 return condition;
53         }
54
55         function sledrun_icon_style(condition, highlight) {
56                 let hl = highlight ? 'h' : 'n';
57                 let src = img_path + '/marker_c_sledrun_' + condition + 'n' + hl + '.png';
58                 return new ol.style.Style({
59                         image: new ol.style.Icon({
60                                 src: src,
61                                 imgSize: [17, 17],
62                                 anchor: [0.5, 0.5]
63                         }),
64                 });
65
66         }
67
68         function sledrun_icon_shadow_style() {
69                 return new ol.style.Style({
70                         image: new ol.style.Icon({
71                                 src: img_path + '/marker_c_shadow.png',
72                                 imgSize: [23, 23],
73                                 anchor: [0.4, 0.4]
74                         }),
75                 });
76         }
77
78         function marker_icon_style(feature) {
79                 let src = img_path + '/marker_p_' + feature.get('type') + '.png';
80                 return new ol.style.Style({
81                         image: new ol.style.Icon({
82                                 src: src,
83                                 imgSize: [20, 34],
84                                 anchor: [0.5, 1.0]
85                         }),
86                 });
87         }
88
89         function point_style(feature, highlight) {
90                 let sledrun = feature.get('type') == 'sledrun';
91                 let icon_style;
92                 if (sledrun) {
93                         let condition = get_sledrun_condition(feature);
94                         icon_style = sledrun_icon_style(condition, highlight);
95                 } else icon_style = marker_icon_style(feature);
96                 if (highlight) {
97                         icon_style.setText(new ol.style.Text({
98                                 text: get_feature_title(feature),
99                                 font: 'icon',
100                                 offsetY: 14,
101                                 stroke: new ol.style.Stroke({
102                                         color: '#ddd',
103                                         width: 2,
104                                 }),
105                         }));
106                 }
107                 if (sledrun) {
108                         let shadow_style = sledrun_icon_shadow_style();
109                         return [shadow_style, icon_style];
110                 }
111                 return [icon_style];
112         }
113
114         function style_point_function(feature, resolution) {
115                 return point_style(feature, false);
116         }
117
118         function style_point_function_highlight(feature, resolution) {
119                 return point_style(feature, true);
120         }
121
122         function style_path_function(feature, resolution) {
123                 let line_color = {
124                         'rodelbahn': '#014e9a',
125                         'gehweg': '#e98401',
126                         'alternative': '#7f7fff',
127                         'lift': '#000000',
128                         'anfahrt': '#e1e100'
129                 };
130                 let color = feature.get('strokeColor') || line_color[feature.get('type')] || '#e7525b';
131                 let width = (feature.get('type') in ['lift', 'anfahrt']) ? 3 : 6;
132                 return new ol.style.Style({
133                         stroke: new ol.style.Stroke({
134                                 color: color,
135                                 width: width
136                         })
137                 });
138         }
139
140         function style_function(feature, resolution) {
141                 if (feature.getGeometry() instanceof ol.geom.Point) return style_point_function(feature, resolution);
142                 return style_path_function(feature, resolution);
143         };
144
145         function style_function_highlight(feature, resolution) {
146                 if (feature.getGeometry() instanceof ol.geom.Point) return style_point_function_highlight(feature, resolution);
147                 return style_path_function(feature, resolution);
148         };
149
150
151         // popup overlay
152         // -------------
153         let popup_container = document.getElementById('popup');
154         let popup_content = document.getElementById('popup-content');
155         let popup_closer = document.getElementById('popup-closer');
156         let popup_overlay = new ol.Overlay({element: popup_container, autoPan: {animation: {duration: 250}}});
157         popup_closer.onclick = function() {popup_overlay.setPosition(undefined); popup_closer.blur(); return false;};
158
159         function create_popup_dom(feature) {
160                 let popup_div = createElement('div');
161
162                 // name
163                 if (feature.get('name') !== undefined && (feature.get('wiki') !== undefined || feature.get('thumb_url') !== undefined)) {
164                         let h2 = appendElement(popup_div, 'h2');
165                         if (feature.get('wiki') === undefined) h2.text(feature.get('name'));
166                         else appendElement(h2, 'a', {href: feature.get('wiki')}).text(feature.get('name'));
167                 }
168
169                 // sledrun information
170                 if (feature.get('type') == 'sledrun') {
171                         let p = appendElement(popup_div, 'p').text('Rodelbahnzustand').append(createElement('br'));
172                         if (feature.get('condition') !== undefined) {
173                                 let condition_text = {1: 'Sehr gut', 2: 'Gut', 3: 'Mittelmäßig', 4: 'Schlecht', 5: 'Geht nicht'};
174                                 let year_month_day = feature.get('date_report').split('-');
175                                 p.append(createElement('a', {href: feature.get('wiki') + '#Eintr.C3.A4ge'}).text(condition_text[feature.get('condition')]), ' ');
176                                 p.append(createElement('small').text(year_month_day[2] + '.' + year_month_day[1] + '.'), ' ');
177                                 p.append(createElement('em').append(createElement('a', {href: feature.get('wiki') + '#Eintragen'}).text('Neu')));
178                         } else {
179                                 p.append(createElement('em').append(createElement('a', {href: feature.get('wiki') + '#Eintragen'}).text('Bitte eintragen')));
180                         }
181                 }
182
183                 // wiki link
184                 if (feature.get('wiki') !== undefined) {
185                         let a = appendElement(appendElement(popup_div, 'p'), 'a', {href: feature.get('wiki')});
186                         let detail_text = 'Details';
187                         if (feature.get('type') == 'sledrun') detail_text += ' zur Rodelbahn';
188                         if (feature.get('type') == 'gasthaus') detail_text += ' zum Gasthaus';
189                         if (feature.get('thumb_url') === undefined) a.text(detail_text);
190                         else a.append(createElement('img', {src: feature.get('thumb_url'), alt: detail_text, title: detail_text}));
191                 }
192
193                 return popup_div;
194         }
195
196
197         // map itself
198         // ----------
199         let lon = json_js.properties.lon;
200         let lat = json_js.properties.lat;
201         let zoom = json_js.properties.zoom;
202         let width = json_js.properties.width;
203         let height = json_js.properties.height;
204         if (zoom === undefined) zoom = 10; // default zoom
205         if (width === undefined) width = '100%'; // default width
206         if (height === undefined) height = 450;  // default: 450 pixel
207         jq_map.width(width);
208         jq_map.height(height);
209
210         let layer_sledrun_source = new ol.source.Vector({features: features_all});
211         let layer_sledrun = new ol.layer.Vector({
212                 source: layer_sledrun_source,
213                 style: style_function
214         });
215
216         let map = new ol.Map({
217                 target: jq_map[0],
218                 layers: [
219                         layer_sledrun
220                 ],
221                 overlays: [popup_overlay],
222                 view: new ol.View({
223                         center: ol.proj.fromLonLat([lon, lat]),
224                         zoom: zoom
225                 }),
226                 controls: ol.control.defaults({
227                         attributionOptions: {
228                                 collapsible: false
229                         }
230                 }),
231                 interactions: ol.interaction.defaults({
232                         mouseWheelZoom: false
233                 })
234         });
235
236
237         let select_hover = new ol.interaction.Select({
238                 condition: ol.events.condition.pointerMove,
239                 style: style_function_highlight,
240         });
241         map.addInteraction(select_hover);
242
243         let select_click = new ol.interaction.Select({
244                 condition: ol.events.condition.click,
245                 style: false,
246         });
247         map.addInteraction(select_click);
248         select_click.on('select', function(event) {
249                 if (event.selected.length > 0) {
250                         let feature = event.selected[0];
251                         let coordinates = feature.getGeometry().getCoordinates();
252                         let popup_dom = create_popup_dom(feature);
253                         if (popup_dom.children().length > 0) {
254                                 $(popup_content).empty().append(popup_dom);
255                                 popup_overlay.setPosition(coordinates);
256                         }
257                 }
258         });
259
260
261         // background layer
262         // ----------------
263
264         // basemap.at layer
265         let capabilitiesUrl = 'https://www.basemap.at/wmts/1.0.0/WMTSCapabilities.xml';
266         fetch(capabilitiesUrl).then(function(response) {
267                 return response.text();
268         }).then(function(text) {
269                 let result = new ol.format.WMTSCapabilities().read(text);
270                 let options = ol.source.WMTS.optionsFromCapabilities(result, {
271                         layer: 'geolandbasemap',
272                         matrixSet: 'google3857',
273                         style: 'normal',
274                 });
275                 let layer_map = new ol.layer.Tile({
276                         source: new ol.source.WMTS(options),
277                 });
278                 map.getLayers().insertAt(0, layer_map);
279         });
280
281         // // Alternatives:
282         // // * OpenTopoMap (see https://opentopomap.org/about)
283         // // * OSM
284         // let layer_map = new ol.layer.Tile({
285         //     source: new ol.source.OSM()
286         // });
287         // map.getLayers().insertAt(0, layer_map);      
288 }
289
290
291 function init_wrmaps() {
292         let jq_maps = $('.wrmap'); // all wrmap <div> elements
293         jq_maps.each(init_wrmap);
294 }
295
296
297 $(document).ready(init_wrmaps);