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