1// (c) 2016-2022 osmcz-app, https://github.com/osmcz/osmcz 2 3 4var osmcz = osmcz || {}; 5osmcz.poiPopup = function (map) { 6 // -- constructor -- 7 8 osmcz._map = map; 9}; 10 11// static fields 12osmcz.permanentlyDisplayed = false; 13osmcz._marker = L.circleMarker([0, 0]); 14osmcz._map = false; 15osmcz.poiPopupOpen = false; 16 17// static methods 18osmcz.poiPopup.load = function (object) { 19 20 // exit when osm object does not exists or osmid is null 21 if ((!object) || (!"id" in object)) { 22 return; 23 } 24 25 $.ajax({ 26 url: 'https://www.openstreetmap.org' + OSM.apiUrl({type: object.type, id: object.id}), 27 dataType: 'xml', 28 jsonp: false, 29 global: false, 30 success: function (data) { 31 console.log("loaded xml", data); 32 33 osmcz.permanentlyDisplayed = true; 34 35 var geojson = osm_geojson.osm2geojson(data); 36 var feature = geojson.features[0]; 37 var tags = feature.properties; 38 feature.properties = { //poloha.net style 39 tags: tags, 40 osm_id: object.id, 41 osm_type: object.type 42 }; 43 44 45 if (feature.geometry.type !== 'Point') { // take centroid of a Polygon 46 var centroid; 47 try { 48 centroid = L.polygon(feature.geometry.coordinates[0]).getBounds().getCenter(); 49 } catch(e) { 50 centroid = L.polygon(feature.geometry.coordinates).getBounds().getCenter(); 51 } 52 53 feature.geometry = { 54 type: 'Point', 55 coordinates: [centroid.lat, centroid.lng] 56 }; 57 } 58 59 if (feature.properties.tags.name) { 60 document.title = feature.properties.tags.name + ' ~ OpenStreetMap.cz'; 61 } 62 63 console.log("geojson feature:", feature); 64 65 // zoom to feature 66 var lon = feature.geometry.coordinates[0]; 67 var lat = feature.geometry.coordinates[1]; 68 osmcz._map.setView([lat, lon]); 69 70 //set icon and show 71 var icon = osmcz.iconsService.get(feature.properties.tags); 72 poiSidebar.setContent(osmcz.poiPopup.getHtml(feature, icon.options.iconUrl)); 73 poiSidebar.show(); 74 osmcz.poiPopupOpen = true; 75 } 76 }); 77 78}; 79 80osmcz.poiPopup.open = function (feature, icon) { //currently from active-layer 81 poiSidebar.setContent(osmcz.poiPopup.getHtml(feature, icon)); 82 poiSidebar.show(); 83 osmcz.poiPopupOpen = true; 84 document.title = 'OpenStreetMap.cz'; 85 if (feature.properties.tags.name) { 86 document.title = feature.properties.tags.name + ' ~ OpenStreetMap.cz'; 87 } 88}; 89 90 91osmcz.poiPopup.close = function () { 92 console.log('poi-popup: close'); 93 94 if (!osmcz.poiPopupOpen) { 95 return; 96 } 97 98 osmcz._map.removeLayer(osmcz._marker); 99 osmcz.permanentlyDisplayed = false; 100 osmcz.poiSidebar.hide(); 101 osmcz.poiPopupOpen = false; 102 103 document.title = 'OpenStreetMap.cz'; 104 105 var path = (location.host === 'openstreetmap.cz' || location.host === 'osm.localhost') 106 ? '/' 107 : location.pathname; 108 109 history.replaceState('', '', path + location.hash); 110}; 111 112osmcz.poiPopup.setUrl = function (p) { 113 var path = (location.host === 'openstreetmap.cz' || location.host === 'osm.localhost') 114 ? ('/' + p.osm_type + '/' + p.osm_id) 115 : ('?' + p.osm_type + '=' + p.osm_id); 116 117 history.replaceState('', '', path + location.hash); 118}; 119 120 121// ------- POI panel template ------- 122osmcz.poiPopup.getHtml = function (feature, icon, embedded) { 123 124 //TODO refactor 125 126 var id = feature.properties.osm_id; 127 var osm_type = feature.properties.osm_type; 128 var general = []; // store k,v objects in array to allow sort them 129 var name = {}; 130 var contact = {}; 131 var openingHours = ''; 132 var payment = {}; 133 var building = {}; 134 var wikidata = {}; 135 var wikimedia = {}; 136 var wikipedia = {}; 137 var guidepost = false; 138 var lon = feature.geometry.coordinates[0]; 139 var lat = feature.geometry.coordinates[1]; 140 141 // Get preferred user language and use it on wikipedia 142 var userLang = (window.navigator.userLanguage || window.navigator.language).split("-")[0]; 143 var wikiLang = (userLang ? "?uselang=" + userLang : "" ); 144 145 // show circle marker 146 if (osmcz.permanentlyDisplayed) { 147 osmcz._marker.setLatLng([lat, lon]).addTo(osmcz._map); 148 } 149 150 var tpl = []; 151 152 // Not needed when we are inside popup 153 if (!embedded) { 154// tpl.push(osmcz.permanentlyDisplayed ? '<a class="close">×</a>' : ''); 155 tpl.push('<h4>'); 156 tpl.push('<img class="poi-icon" src="' + icon + '"> '); 157 tpl.push('<span class="h4-text">' + (feature.properties.tags.name || 'Bod zájmu') + '</span>'); 158 tpl.push('</h4>'); 159 } 160 161 $.each(feature.properties.tags, function (k, v) { 162 k = k + ""; 163 v = v + ""; 164 165 if (k.match(/^name:/)) 166 name[k] = v; 167 else if (k.match(/^payment:/)) 168 payment[k] = v; 169 else if (k.match(/^contact:/) || k.match(/^phone/) || k.match(/^email/)) 170 contact[k] = v; 171 else if (k.match(/^building/)) 172 building[k] = v; 173 else if (k.match(/^opening_hours$/)) 174 openingHours = v; 175 else if (k.match(/^addr:|^ref:ruian:/)) //skip TODO show in expert mode 176 undefined; 177 else 178 general.push({k: k, v: v}); 179 180 // global flags 181 if (k.match(/^wikipedia/)) { 182 // Prefer wikipedia over wikipedia:cs over first one 183 if (k.match(/^wikipedia$/)) 184 wikipedia = {k: k, v: v}; 185 else if (k.match(/^wikipedia:cs$/) && 186 (!wikipedia.k || (wikipedia.k && !wikipedia.k == "wikipedia"))) 187 wikipedia = {k: k, v: k.split(":").pop() + ":" + v}; 188 else if (!wikipedia.k && k.match(/^wikipedia:/)) 189 wikipedia = {k: k, v: k.split(":").pop() + ":" + v}; 190 } 191 else if (k.match(/^wikimedia_commons/) && v.match(/^File:/)) 192 wikimedia = {k: k, v: v}; 193 else if (k.match(/^wikidata/)) 194 wikidata = {k: k, v: v}; 195 else if (k.match(/^image$/) && v.match(/^File:/)) 196 wikimedia = {k: 'wikimedia_commons', v: v}; 197 }); 198 199 // sort the array 200 general.sort(function (a, b) { 201 return a.k.localeCompare(b.k); 202 }); 203 204 if (general.length) { 205 for (var i in general) { 206 var k = general[i].k; 207 var v = general[i].v; 208 tpl.push('<b>' + k + '</b> = '); 209 // wikipedia=* or xxx:wikipedia=* 210 if (k.match(/^wikipedia$/) || k.match(/:wikipedia$/)) { 211 if (v.match(/:/)) { 212 tpl.push('<a href="https://' + v.split(":")[0] + '.wikipedia.org/wiki/' + v.split(":")[1] + wikiLang + '">' + v + '</a>'); 213 } else { 214 tpl.push('<a href="https://wikipedia.org/wiki/' + v + wikiLang + '">' + v + '</a>'); 215 } 216 } 217 // wikipedia:<country>=* or xxx:wikipedia:<country>=* 218 else if (k.match(/^wikipedia:/) || k.match(/:wikipedia:/)) 219 tpl.push('<a href="https://' + k.split(":").pop() + '.wikipedia.org/wiki/' + v + wikiLang + '">' + v + '</a>'); 220 // wikidata=* 221 else if (k.match(/^wikidata$/)) 222 tpl.push('<a href="https://www.wikidata.org/wiki/' + v + wikiLang + '">' + v + '</a>'); 223 // wikimedia commons=* 224 else if (k.match(/^wikimedia_commons$/)) 225 tpl.push('<a href="https://commons.wikimedia.org/wiki/' + v + wikiLang + '">' + v + '</a>'); 226 else if (k.match(/^image$/) && v.match(/^File:/)) 227 // handle image as wikimedia_commons 228 tpl.push('<a href="https://commons.wikimedia.org/wiki/' + v + wikiLang + '">' + v + '</a>'); 229 // Katalog národnÃho památkového ústavu 230 else if (k.match(/^ref:npu$/)) 231 tpl.push('<a href="http://pamatkovykatalog.cz/?mode=parametric&isProtected=1&presenter=ElementsResults&indexId=' + encodeURIComponent(v) + '">' + v + '</a>'); // @TODO: upravit, až bude HTTPS verze 232 else 233 // Just standard url 234 tpl.push(v.match(/^https?:\/\/.+/) ? ('<a href="' + v + '">' + v + '</a>') : v); 235 tpl.push('<br>'); 236 } 237 } 238 239 240 var section = function (obj, label, hide) { 241 if (Object.keys(obj).length) { 242 hide && tpl.push('<p><b>' + label + '</b> <a href="#" onclick="$(this).parent().hide().next().show();return false">zobrazit</a></p>'); 243 hide && tpl.push('<div style="display:none">'); 244 hide && tpl.push('<h5>' + label + ' <a href="#" onclick="$(this).parent().parent().hide().prev().show();return false">skrýt</a></h5>'); 245 246 hide || tpl.push('<h5>' + label + '</h5>'); 247 $.each(obj, function (k, v) { 248 tpl.push('<b>' + k + '</b> = '); 249 tpl.push(v.match(/^https?:\/\/.+/) ? ('<a href="' + v + '">' + v + '</a>') : v); 250 tpl.push('<br>'); 251 }); 252 tpl.pop(); //remove last br 253 254 hide && tpl.push('</div>'); 255 } 256 }; 257
258 section(name, 'Dalšà jména:', true); 259 try { 260 if (openingHours != '') { 261 console.log(openingHours); 262 tpl = tpl.concat(osmcz.openingHoursService.getHtml(openingHours)); 263 } 264 } catch (err) { 265 tpl.push("<b>opening_hours</b> = " + openingHours + "<br> <em>(NepodaÅilo se naparsovat: " + err + ")</em>"); 266 } 267 section(payment, 'Možnosti platby:'); 268 section(contact, 'Kontakty:'); 269 section(building, 'Budova:'); 270 271 // Finish there when we are inside popup 272 if (embedded) { 273 return tpl.join(''); 274 } 275 276 //tpl.push('<div class="osmid"><a href="http://osm.org/' + osm_type + '/' + id + '">osm ID: ' + osm_type + '/' + id + '</a></div>'); 277 tpl.push('<div class="osmid"><a href="https://osmap.cz/' + osm_type + '/' + id + '">osmap.cz/' + osm_type + '/' + id + '</a></div>'); 278 tpl.push('<div id="wikimedia-commons" data-osm-id="' + id + '"></div>'); 279 tpl.push('<div id="guidepost" data-osm-id="' + id + '"></div>'); 280 tpl.push('<div id="mapillary-photo" data-osm-id="' + id + '"></div>'); 281 282 283 // ---------------------------------------- images ------------------------------------------ 284 // TODO refactor 285 286 // ----------------- WP & WM ----------------- 287 288 if (feature.wikimedia || feature.wikipedia) { 289 setTimeout(function () { //after dom is created 290 showWikimediaCommons(); 291 }, 0); 292 } 293 294 // url of ajax proxy server for wikipedia and wikimedia (CORS not HTTPS) 295 var xhd_proxy_url = 'https://openstreetmap.cz/xhr_proxy.php'; 296 297 // Show picture from wikimedia commons if there is `wikidata` or `wikimedia_commons` or `wikipedia` tag 298 // Priorities: 299 // 1) wikidata 300 // 2) wikimedia_commons 301 // 3) wikipedia 302 // 303 // There is also fallback - if wikidata reply does not contains image and wikimedia_commons or wikipedia 304 // tags exists, they will be checked as well. 305 306 if (wikimedia.k || wikipedia.k || wikidata.k) { 307 var url; 308 if (wikidata.k) 309 url = getWikiApiUrl(wikidata); 310 else if (wikimedia.k) 311 url = getWikiApiUrl(wikimedia); 312 else 313 url = getWikiApiUrl(wikipedia); 314 315 $.ajax({ 316 url: xhd_proxy_url, 317 data: { 318 url: url 319 }, 320 dataType: 'json', 321 success: function (data) { 322 var replyType = getWikiType(data); 323 if (replyType == 'wikimedia') { 324 feature.wikimedia = data; 325 showWikimediaCommons(); 326 } 327 else if (replyType == 'wikipedia') { 328 feature.wikipedia = data; 329 showWikimediaCommons(); 330 } 331 else if (replyType == 'wikidata') { 332 var reply = processWikidataReply(data); 333 if (!reply.k) {
334 // Wikidata entry does not contains image 335 // try other tags 336 if (wikimedia.k) 337 reply = wikimedia; 338 else if (wikipedia.k) 339 reply = wikipedia; 340 } 341 342 // get image thumbnail url or try wikimedia/wikipedia if there is no image on wikidata 343 if (reply.k) { 344 var url = getWikiApiUrl(reply); 345 $.ajax({ 346 url: xhd_proxy_url, 347 data: { 348 url: url 349 }, 350 dataType: 'json', 351 success: function (data) { 352 if (getWikiType(data) == 'wikimedia') { 353 feature.wikimedia = data; 354 showWikimediaCommons(); 355 } else if (getWikiType(data) == 'wikipedia') { 356 feature.wikipedia = data; 357 showWikimediaCommons(); 358 } 359 } 360 }); 361 } 362 } 363 } 364 }); 365 } 366 367 // Show image from wikimedia commons 368 function showWikimediaCommons() { 369 370 // Template 371 var wcmTpl = '<h5><a href="https://commons.wikimedia.org/">' 372 + '<img class="commons_logo" src="' + osmcz.basePath + 'img/commons_logo.png" height="24"></a>' 373 + 'Foto z Wikipedie</h5>' 374 + '<a href="_descriptionshorturl">' 375 + '<img src="_thumburl" >' 376 + '</a>'; 377 378 var wcm = $('#wikimedia-commons'); 379 if (id == wcm.attr('data-osm-id') && (feature.wikimedia || feature.wikipedia)) { 380 if (feature.wikimedia) { 381 var k = Object.keys(feature.wikimedia.query.pages)[0]; 382 if (!feature.wikimedia.query.pages[k].imageinfo.length) 383 return; 384 var descriptionshorturl = feature.wikimedia.query.pages[k].imageinfo[0].descriptionshorturl; 385 var thumburl = feature.wikimedia.query.pages[k].imageinfo[0].thumburl; 386 } else { 387 var k = Object.keys(feature.wikipedia.query.pages)[0]; 388 if (!feature.wikipedia.query.pages[k].pageimage) 389 return; 390 var descriptionshorturl = 'https://commons.wikimedia.org/wiki/File:' + feature.wikipedia.query.pages[k].pageimage; 391 var thumburl = feature.wikipedia.query.pages[k].thumbnail.source; 392 } 393 wcm.html(wcmTpl.replace(/_thumburl/g, thumburl).replace(/_descriptionshorturl/g, descriptionshorturl)); 394 } 395 } 396 397 // ----------------- GUIDEPOST ----------------- 398 399 400 // show guidepost picture from openstreetmap.cz 401 if (feature.properties.tags.information == 'guidepost') { 402 if (feature.guidepost) { 403 setTimeout(function () { //after dom is created 404 showGuidepost(); 405 }, 0); 406 } 407 else { 408 var ref = feature.properties.tags.ref; 409 $.ajax({ 410 url: osmcz.photoDbUrl + 'api/close', 411 data: { 412 lat: lat, 413 lon, lon, 414 limit: '1', 415 distance: '50' 416 }, 417 dataType: 'json', 418 success: function (data) { 419 feature.guidepost = data; 420 showGuidepost(); 421 } 422 }); 423 } 424 } 425 426 var gpTpl = '<h5>Foto rozcestnÃku</h5>' 427 + '<a href="_imgUrl">' 428 + '<div id="thumbnailLoadSpinner" class="text-center"><span class="glyphicon glyphicon-refresh text-info gly-spin"></span><br></div>' 429 + '<img id="thumbnailImage" src="" class="center-block"/>' 430 + '</a>' 431 + '<div class="margin-top-05"><b>Fotografii poskytl: </b> _autor' 432 + '<span style="margin: 0.5em"/>' 433 + ' <a href="'+osmcz.photoDbUrl+'?id=_id" target="_blank" class="btn btn-default btn-xs">' 434 + ' <span class="glyphicon glyphicon-pencil" title="upravit"></span> upravit</a>' 435 + '</div>' 436 437 function showGuidepost() { 438 var gp = $('#guidepost'); 439 if (id == gp.attr('data-osm-id') && feature.guidepost) { 440 // TODO: show all guideposts 441 if (!feature.guidepost.features.length) 442 return; 443 var autor = feature.guidepost.features[0].properties.author; 444 var gpostId = feature.guidepost.features[0].properties.id; 445 var fullImgUrl = osmcz.photoDbUrl + 'files/' + gpostId + '.jpg'; 446 gp.html(gpTpl.replace(/_autor/g, autor).replace(/_imgUrl/g, fullImgUrl).replace(/_id/g, gpostId)); 447 448 // get guidepost thumbnail from photodb cache server first 449 // if it fails, request it from phpThumb 450 var tb = new Image(); 451 tb.src = osmcz.photoDbUrl + "files/250px/" + gpostId + '.jpg'; 452 tb.onload = function () { 453 $('#thumbnailLoadSpinner').hide(); 454 $('#thumbnailImage').attr('src', tb.src); 455 }; 456 tb.onerror = function () { 457 $('#thumbnailLoadSpinner').html('<br><span class="glyphicon glyphicon-picture bigger semigrey thumbnail crossed" title="Náhled nenà k dispozici."><span><br>'); 458 $('#thumbnailLoadSpinner').attr('class', 'text-nowrap text-center'); 459 460 }; 461 462 } 463 } 464 465 // ----------------- MAPILLARY ----------------- 466 467 // show closest mapillary photo if no photo so far 468 if (osmcz.permanentlyDisplayed) { 469 if (!feature.wikimedia && !feature.wikipedia && !feature.guidepost) { 470 //TODO WP tag != existing WP photo --> chain mapillary after the WP request? 471 472 if (feature.mapillary) { 473 setTimeout(function () { //after dom is created 474 showMapillary(); 475 }, 0); 476 } 477 else { 478 //TODO - limit=10 & choose the best oriented photo 479 $.ajax({ 480 url: 'https://api.mapillary.com/v1/im/close?lat=' + lat + '&lon=' + lon + '&distance=30&limit=1', 481 dataType: 'json', 482 jsonp: false, 483 global: false, 484 success: function (data) { 485 feature.mapillary = data; 486 showMapillary(); 487 } 488 }); 489 } 490 } 491 } 492 493 // "_key" se nahrazuje mapillary IDÄkem 494 var mpTpl = '<h5>Nejbližšà street-view</h5>' 495 + '<a href="https://www.mapillary.com/map/im/_key/photo">' 496 + '<img src="' + osmcz.fakeHttps + 'images.mapillary.com/_key/thumb-320.jpg" height="187">' //@todo https viz https://github.com/mapillary/mapillary_issues/issues/2419 497 + '</a>'; 498 499 function showMapillary() { 500 var mp = $('#mapillary-photo'); 501 if (id == mp.attr('data-osm-id') && feature.mapillary.length) {
502 mp.html(mpTpl.replace(/_key/g, feature.mapillary[0].key)); 503 } 504 } 505 506 return tpl.join(''); 507 508 509 // ------- Wikidata, wikimedia commons and wikipedia functions ------- 510 // 511 // Prepare correct api request 512 function getWikiApiUrl(we) { 513 if (we.k == "wikimedia_commons") { // wikimedia commons tag 514 return 'https://commons.wikimedia.org/w/api.php?action=query&prop=imageinfo&iiprop=url&iiurlwidth=240&format=json' 515 + '&titles=' + encodeURIComponent(we.v); 516 } else if (we.k == "wikidata") { // wikidata tag 517 return 'https://www.wikidata.org/w/api.php?action=wbgetclaims&property=P18&format=json' 518 + '&entity=' + encodeURIComponent(we.v); 519 } else { // wikipedia tag 520 var country = we.v.split(":")[0]; 521 if (country === we.v) 522 country = "en"; 523 return 'https://' + country + '.wikipedia.org/w/api.php?action=query&prop=pageimages&pithumbsize=240&format=json' 524 + '&titles=' + encodeURIComponent(we.v); 525 } 526 } 527 528 // Analyze reply and identify wikidata / wikimedia / wikipedia content 529 function getWikiType(d) { 530 if (d.query) { 531 if (Object.keys(d.query.pages)[0]) { 532 var k = Object.keys(d.query.pages)[0]; 533 if (d.query.pages[k].imageinfo) 534 return 'wikimedia'; 535 if (d.query.pages[k].pageimage) 536 return 'wikipedia'; 537 } 538 } 539 if (d.claims) 540 return 'wikidata'; 541 } 542 543 // In wikidata entry is image name. but we need thumbnail, 544 // so we will convert wikidata reply to wikimedia_commons tag 545 function processWikidataReply(d) { 546 var w = {}; 547 if (d.claims.P18) { 548 w.k = 'wikimedia_commons'; 549 w.v = 'File:' + d.claims.P18[0].mainsnak.datavalue.value; 550 } 551 return w; 552 } 553 554};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.