9 changed files with 271 additions and 12 deletions
-
4bin/download_dependencies
-
52bin/tag2link-converter
-
7lib/tag2link-sophox.qry
-
15lib/tag2link-wikidata.qry
-
23src/category.css
-
15src/index.js
-
63src/tagsDisplay-tag2link.js
-
96src/tagsDisplay.js
-
8src/wikipedia.js
@ -1,3 +1,7 @@ |
|||
#!/bin/sh |
|||
|
|||
curl -H "Accept: application/json" -H "Content-Type: application/sparql-query" -H "User-Agent: OpenStreetBrowser" -XPOST -d @'lib/tag2link-wikidata.qry' https://query.wikidata.org/sparql > data/tag2link-wikidata.json |
|||
curl -H "Accept: application/json" -H "Content-Type: application/sparql-query" -H "User-Agent: OpenStreetBrowser" -XPOST -d @'lib/tag2link-sophox.qry' https://sophox.org/sparql > data/tag2link-sophox.json |
|||
bin/tag2link-converter |
|||
|
|||
bin/download_geoip2 |
@ -0,0 +1,52 @@ |
|||
#!/usr/bin/php |
|||
<?php |
|||
$tag2link = array(); |
|||
|
|||
$files = array('data/tag2link-wikidata.json', 'data/tag2link-sophox.json'); |
|||
foreach ($files as $file) { |
|||
$data = json_decode(file_get_contents($file), true); |
|||
|
|||
foreach ($data['results']['bindings'] as $entry) { |
|||
$key = substr($entry['OSM_key']['value'], 4); |
|||
$link = $entry['formatter_URL']['value']; |
|||
|
|||
if (array_key_exists($key, $tag2link)) { |
|||
// avoid duplicates |
|||
$duplicates = array_filter($tag2link[$key]['formatter'], function ($e) use ($link) { |
|||
return $e['link'] === $link; |
|||
}); |
|||
|
|||
if (sizeof($duplicates)) { |
|||
if (array_key_exists('operatorLabel', $entry)) { |
|||
foreach ($duplicates as $i => $d) { |
|||
$tag2link[$key]['formatter'][$i]['operator'] = $entry['operatorLabel']['value']; |
|||
} |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
} |
|||
else { |
|||
$tag2link[$key] = array( |
|||
'label' => $entry['itemLabel']['value'], |
|||
'formatter' => array(), |
|||
); |
|||
} |
|||
|
|||
$formatter = array( |
|||
'link' => $link, |
|||
); |
|||
|
|||
if (array_key_exists('operatorLabel', $entry)) { |
|||
$formatter['operator'] = $entry['operatorLabel']['value']; |
|||
print "{$formatter['operator']}\n"; |
|||
} |
|||
else if (preg_match("/^https?:\/\/([^\/]*)(\/.*|)$/", $link, $m)) { |
|||
$formatter['operator'] = $m[1]; |
|||
} |
|||
|
|||
$tag2link[$key]['formatter'][] = $formatter; |
|||
} |
|||
} |
|||
|
|||
file_put_contents('dist/tag2link.json', json_encode($tag2link, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)); |
@ -0,0 +1,7 @@ |
|||
SELECT ?item ?itemLabel (CONCAT("Key:", ?permanent_key_ID) as ?OSM_key) ?formatter_URL WHERE { |
|||
FILTER(?permanent_key_ID NOT IN ('image', 'url', 'website', 'wikidata', 'wikimedia_commons')). |
|||
?item osmdt:P2 osmd:Q7. |
|||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } |
|||
?item osmdt:P16 ?permanent_key_ID. |
|||
?item osmdt:P8 ?formatter_URL. |
|||
} |
@ -0,0 +1,15 @@ |
|||
SELECT ?itemLabel ?OSM_key ?formatter_URL ?operatorLabel WHERE { |
|||
?item wdt:P1282 ?OSM_key . |
|||
FILTER(?OSM_key NOT IN("Key:image", "Key:url", "Key:website", "Key:wikidata", "Key:wikimedia_commons")) |
|||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } |
|||
{ |
|||
?item p:P1630 ?statement. |
|||
?statement ps:P1630 ?formatter_URL. |
|||
} |
|||
UNION |
|||
{ |
|||
?item p:P3303 ?statement. |
|||
?statement ps:P3303 ?formatter_URL. |
|||
} |
|||
OPTIONAL { ?statement pq:P137 ?operator. } |
|||
} |
@ -0,0 +1,63 @@ |
|||
const httpGet = require('./httpGet') |
|||
const formatter = require('./tagsDisplay').formatter |
|||
|
|||
let tag2link |
|||
|
|||
register_hook('init_callback', (initState, callback) => { |
|||
httpGet('dist/tag2link.json', {}, (err, result) => { |
|||
if (err) { |
|||
console.error('Can\'t read dist/tag2link.json - execute bin/download_dependencies') |
|||
return callback() |
|||
} |
|||
|
|||
tag2link = JSON.parse(result.body) |
|||
|
|||
Object.keys(tag2link).forEach(key => { |
|||
let tag = tag2link[key] |
|||
let link = tag.formatter[0].link.replace('$1', '{{ value }}') |
|||
|
|||
if (tag.formatter.length > 1) { |
|||
link = "#\" onclick=\"return tag2link(this, " + JSON.stringify(key).replace(/"/g, '"') + ", {{ value|json_encode }})" |
|||
} |
|||
|
|||
formatter.push({ |
|||
regexp: new RegExp("^" + key + "$"), |
|||
link |
|||
}) |
|||
}) |
|||
|
|||
callback() |
|||
}) |
|||
}) |
|||
|
|||
global.tag2link = function (dom, key, value) { |
|||
let div = document.createElement('div') |
|||
div.className = 'tag2link' |
|||
dom.parentNode.appendChild(div) |
|||
|
|||
let closeButton = document.createElement('div') |
|||
closeButton.className = 'closeButton' |
|||
closeButton.innerHTML = '❌' |
|||
closeButton.onclick = () => { |
|||
dom.parentNode.removeChild(div) |
|||
} |
|||
div.appendChild(closeButton) |
|||
|
|||
let selector = document.createElement('ul') |
|||
div.appendChild(selector) |
|||
|
|||
let tag = tag2link[key] |
|||
tag.formatter.forEach(formatter => { |
|||
let li = document.createElement('li') |
|||
|
|||
let a = document.createElement('a') |
|||
a.target = '_blank' |
|||
a.href = formatter.link.replace('$1', value) |
|||
a.appendChild(document.createTextNode(formatter.operator)) |
|||
|
|||
li.appendChild(a) |
|||
selector.appendChild(li) |
|||
}) |
|||
|
|||
return false |
|||
} |
@ -0,0 +1,96 @@ |
|||
const OverpassLayer = require('overpass-layer') |
|||
|
|||
const formatter = [ |
|||
{ |
|||
regexp: /^(.*:)?wikidata$/, |
|||
link: 'https://wikidata.org/wiki/{{ value }}' |
|||
}, |
|||
{ |
|||
regexp: /^(.*:)?wikipedia$/, |
|||
link: '{% set v = value|split(":") %}https://{{ v[0] }}.wikipedia.org/wiki/{{ v[1]|replace({" ": "_"}) }}' |
|||
}, |
|||
{ |
|||
regexp: /^(.*:)?wikipedia:([a-zA-Z]+)$/, |
|||
link: '{% set v = key|matches(":([a-zA-Z]+)") %}https://{{ v[1] }}.wikipedia.org/wiki/{{ value|replace({" ": "_"}) }}' |
|||
}, |
|||
{ |
|||
regexp: /^((.*:)?website(:.*)?|(.*:)?url(:.*)?|contact:website)$/, |
|||
link: '{{ value|websiteUrl }}' |
|||
}, |
|||
{ |
|||
regexp: /^(image|wikimedia_commons)$/, |
|||
link: '{% if value matches "/^(File|Category):/" %}' + |
|||
'https://commons.wikimedia.org/wiki/{{ value|replace({" ": "_"}) }}' + |
|||
'{% else %}' + |
|||
'{{ value|websiteUrl }}' + |
|||
'{% endif %}' |
|||
}, |
|||
{ |
|||
regexp: /^(species)$/, |
|||
link: 'https://species.wikimedia.org/wiki/{{ value|replace({" ": "_"}) }}' |
|||
}, |
|||
{ |
|||
regexp: /^(phone|contact:phone|fax|contact:fax)(:.*|)$/, |
|||
link: 'tel:{{ value }}' |
|||
}, |
|||
{ |
|||
regexp: /^(email|contact:email)(:.*|)$/, |
|||
link: 'mailto:{{ value }}' |
|||
} |
|||
] |
|||
|
|||
let compiled = false |
|||
let defaultTemplate |
|||
|
|||
function tagsDisplay (tags) { |
|||
if (!compiled) { |
|||
defaultTemplate = OverpassLayer.twig.twig({ data: '{{ value }}', autoescape: true }) |
|||
for (let i in formatter) { |
|||
if (formatter[i].format) { |
|||
formatter[i].template = OverpassLayer.twig.twig({ data: formatter[i].format, autoescape: true }) |
|||
} else { |
|||
formatter[i].template = OverpassLayer.twig.twig({ data: '<a target="_blank" href="' + formatter[i].link + '">{{ value }}</a>', autoescape: true }) |
|||
} |
|||
} |
|||
|
|||
compiled = true |
|||
} |
|||
|
|||
const div = document.createElement('dl') |
|||
div.className = 'tags' |
|||
for (let k in tags) { |
|||
const dt = document.createElement('dt') |
|||
dt.appendChild(document.createTextNode(k)) |
|||
div.appendChild(dt) |
|||
|
|||
let template = defaultTemplate |
|||
|
|||
const dd = document.createElement('dd') |
|||
for (let i = 0; i < formatter.length; i++) { |
|||
if (k.match(formatter[i].regexp)) { |
|||
template = formatter[i].template |
|||
break |
|||
} |
|||
} |
|||
|
|||
let value = tags[k].split(/;/g) |
|||
value = value.map(v => { |
|||
// trim whitespace (but add it around the formatted value later)
|
|||
let m = v.match(/^( *)([^ ].*[^ ]|[^ ])( *)$/) |
|||
if (m) { |
|||
return m[1] + template.render({ key: k, value: m[2] }) + m[3] |
|||
} |
|||
return v |
|||
}).join(';') |
|||
|
|||
dd.innerHTML = value |
|||
div.appendChild(dd) |
|||
} |
|||
|
|||
return div |
|||
} |
|||
|
|||
module.exports = { |
|||
display: tagsDisplay, |
|||
formatter |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue