Module:OSM

From blackwiki
Jump to navigation Jump to search

Goal

On this page Wikipedia:WikiProject_OpenStreetMap it becomes clear different approaches have been tried before to link from Wikipedia to Openstreetmap.

But why is this so hard? Well, there are a few problems, one of them is that ids for nodes, ways and relations on Openstreetmap are not guaranteed to be stable. When ways are split the id will point to a shorter stretch and the other part will get a new id. Sometimes points of interest which start as nodes, are converted to (closed) ways. Some contributors delete items, which get recreated by them a bit further along. Or somebody notices it's missing and recreates it fresh.

So pointing directly to the OSM objects themselves doesn't work all that well. Let's say it's not future proof. It may work at the moment you define the link and a good while after, or it may fail the next day already.

Wikidata

Luckily Wikidata came along and this forms a part of the puzzle that can help solve this problem. What we can do is add tags which refer back to Wikidata items on the Openstreetmap side.

wikidata tag

Now if a way is split, both parts will continue with the proper wikidata tag.

Overpass API

But now we have a new problem. How to find those elements in the Openstreetmap DB, which got wikidata tags assigned to them?

Well there is a tool to help with this. It has the whole database of Openstreetmap, updated on a minutely basis and it's fast to query, on condition the queries can be kept simple. It's called Overpass API.

Overpass Turbo

Looking at raw data, is not very instructive either, so another tool was built around it, which is called Overpass Turbo.

Query

Developing these queries takes some trial and error. What this module does for you, is to automate the process and come up with a query that works, with sensible default values.

For example if you omit the Wikidata id, the id of the corresponding article will be used.

But it's also possible to write your own custom queries, or to specify further filters. Normally all objects' types, nodes, ways and relations will be fetched, but it's possible to limit to only ways, for example.

in style

Adding a style sheet allows you to present the data in a more suitable way. An attempt is being made to add a style sheet that makes sense, but it's possible to define your own.

Usage

  • Search Openstreetmap independently of wikidata tags
{{#invoke:OSM|overpass}}
  • Search Openstreetmap for items which have wikidata=Q....
{{#invoke:OSM|wd}}
  • Search Openstreetmap for items which have name:etymology:wikidata=Q....
{{#invoke:OSM|etym}}
  • Search Openstreetmap for items which have subject:wikidata=Q....
{{#invoke:OSM|subject}}
  • Search Openstreetmap for items which have artist:wikidata=Q....
{{#invoke:OSM|artist}}
  • Search Openstreetmap for items which have architect:wikidata=Q....
{{#invoke:OSM|architect}}
  • Search Openstreetmap for items which have operator:wikidata=Q....
{{#invoke:OSM|operator}}
  • Search Openstreetmap for items which have brand:wikidata=Q....
{{#invoke:OSM|brand}}
  • Search Openstreetmap for public transport lines related to the current article
{{#invoke:OSM|pt}}

Parameters

  • display : Text shown on outgoing link
  • link : defaults to yes, but can be set to no for use in other templates
  • timeout : defaults to 15 seconds, needs to be set higher for more complicated queries
  • id : can link to a 1 or more Q-numbers of wikidata items, defaults to the Q-number id of the calling page
  • query : makes it possible to extend the query, which can be used to limit the search to highways or tombstones
  • prim : only fetch node, way and/or relation primitives
  • coord : coordinates and a zoom factor in this format: 47.0618984;15.4370248;17 from which a bbox will be determined.
  • limitToBBOX: defaults to yes. If coord is set the query is limited to the resulting bbox
  • run : defaults to yes. Use no if you want the user to be able to move the map or change the query before searching
  • style : it's possible to define a MapCSS style to present the result in a nicer way

Test values

8 tests failed.

test_brand
Text Expected Actual
☑Y {{#invoke:OSM|brand|id=Q3195848|display=Toon vestigingen op een kaart|style=node{ text: name; icon-image: url('https://upload.wikimedia.org/wikipedia/commons/0/07/Kewlox_logo.png'); icon-width: 32;} }} Toon vestigingen op een kaart Toon vestigingen op een kaart
  1. REDIRECT Template:Xmark
{{#invoke:OSM|brand|id=Q3195848|display=Show locations on map}} Show locations on map Show locations on map
test_etym
Text Expected Actual
  1. REDIRECT Template:Xmark
{{#invoke:OSM|etym|id=Q2360250|display=Map}} Map Map
  1. REDIRECT Template:Xmark
{{#invoke:OSM|etym|id=Q118958|display=Streets named after Leuven|query=[highway]|timeout=50|coord=50.879;4.701;9}} Streets named after Leuven Streets named after Leuven
  1. REDIRECT Template:Xmark
{{#invoke:OSM|etym|id=Q13104397|link=no|display=Show avenue named after Numa Ensch Tesch on Openstreetmap}} http://overpass-turbo.eu/?Q=%5Btimeout%3A20%5D%5Bout%3Ajson%5D%3B%0A%28%0Anode%5B%22name%3Aetymology%3Awikidata%22~%22%28%5E%7C%3B%29%28Q13104397%29%28%3B%7C%24%29%22%5D%3B%0Away%5B%22name%3Aetymology%3Awikidata%22~%22%28%5E%7C%3B%29%28Q13104397%29%28%3B%7C%24%29%22%5D%3B%0Arelation%5B%22name%3Aetymology%3Awikidata%22~%22%28%5E%7C%3B%29%28Q13104397%29%28%3B%7C%24%29%22%5D%3B%0A%3E%3E%3B%0A%29%3B%0Aout%20geom%3B%0A%7B%7Bstyle%3A%0Anode%20%5Bname%3Aetymology%3Awikidata%5D%7B%0A%20%20text%3A%20name%3B%0A%20%20icon-width%3A%2032%3B%7D%0A%7D%7D%0A&R http://overpass-turbo.eu/?Q=%5Btimeout%3A20%5D%5Bout%3Ajson%5D%3B%0A%28%0Anode%5B%22name%3Aetymology%3Awikidata%22~%22%28%5E%7C%3B%29%28Q13104397%29%28%3B%7C%24%29%22%5D%3B%0Away%5B%22name%3Aetymology%3Awikidata%22~%22%28%5E%7C%3B%29%28Q13104397%29%28%3B%7C%24%29%22%5D%3B%0Arelation%5B%22name%3Aetymology%3Awikidata%22~%22%28%5E%7C%3B%29%28Q13104397%29%28%3B%7C%24%29%22%5D%3B%0A%3E%3E%3B%0A%29%3B%0Aout%20geom%3B%0A&R
test_pt
Text Expected Actual
☑Y {{#invoke:OSM|pt|id=Q18012220|display=De Lijn 352 on map}} De Lijn 352 on map De Lijn 352 on map
test_subject
Text Expected Actual
  1. REDIRECT Template:Xmark
{{#invoke:OSM|subject|id=Q1356304|display=Statues of Ernest Claes|coord=51.1936;3.23812;18}} Statues of Ernest Claes Statues of Ernest Claes
test_wd
Text Expected Actual
  1. REDIRECT Template:Xmark
{{#invoke:OSM|wd|id=Q650206;Q713300|display=Vresse and Florenville}} Vresse and Florenville Vresse and Florenville
  1. REDIRECT Template:Xmark
{{#invoke:OSM|wd|id=Q20748715|display=Church of Vresse|coord=49.71513;5.23446;14}} Church of Vresse Church of Vresse
  1. REDIRECT Template:Xmark
{{#invoke:OSM|wd|id=Q1716059|prim=w|display=Cemetery of Arlon on a map}} Cemetery of Arlon on a map Cemetery of Arlon on a map



local function buildquery(frame, target)
	local textToDisplay, leftLink, rightLink, runQueryRightAway, itemObject, itemID, query, coord, bbox, remark, overpassUrl, primitives, timeout, style, logo
	local args = frame.args
	
	if args.display then
		-- text to display on link
		textToDisplay = ' ' .. args.display
	else
		textToDisplay = ''
	end
	if args.timeout then
		timeout = '[timeout:' .. args.timeout .. '][out:json];\n'
	else
		timeout = '[timeout:20][out:json];\n'
	end
	if args.link and args.link == 'no' then
		-- just return the url
		leftLink = ''
		rightLink = ''
		textToDisplay = ''
	else
		leftLink = '['
		rightLink = ']'
	end
	if args.run and args.run == 'no' then
		-- don't run immediately
		runQueryRightAway = ''
	else
		runQueryRightAway = '&R'
	end
	if frame.args.overpass then
		itemID = ""
		itemObject = nil
	else
		if args.id then
			-- build query for specific Q-item(s) 
			itemID = args.id
			pcall(function () itemObject = mw.wikibase.getEntityObject(mw.text.split(itemID,";")[1]) end)
		else
			itemObject = mw.wikibase.getEntityObject()
			if itemObject == nil then
				return "This page doesn't have a wikidata entry"
			end
			-- build query for current page
			itemID = itemObject.id
		end
	end
	-- Always perform a regular expression based search
    -- The data may contain multiple values
    if frame.args.overpass == nil then
		itemID = '"~"(^|;)(' .. itemID:gsub(";", "\|") .. ')(;|$)'
		leftbracket = '["'
		rightbracket = '"]'
	else
		leftbracket = ''
		rightbracket = ''
	end

	if args.query then
		-- user can add their own tags to filter on
		query = args.query
	else
		if frame.args.overpass then
			return "If you invoke with overpass, you have to include a query="
		end
		query = ''
	end
	if args.coord and not(args.limitToBBOX=='no') then
		-- The user can provide coordinates and a zoom factor
		coord = '&C=' .. args.coord
		-- In that case we can limit the search to the area in view
		bbox = '({{bbox}})'
		-- and tell them how to search wider.
		remark = ' // remove the ' .. bbox .. 'if you want the query to be executed globally'
	else
		coord = ''
		bbox = ''
		remark = ''
	end
	overpassUrl = timeout .. '(\n'
	-- if the user specifies prim(itives), but then leaves the string empty, abort
	if args.prim then
		if args.prim == '' then
			return "Please indicate which primitives you want to query for"
		end
		primitives = args.prim
	else
		primitives = 'nwr'
	end

	if primitives:find("n") then
		-- Include nodes
		overpassUrl = overpassUrl .. 'node' .. leftbracket .. target .. itemID .. rightbracket .. query .. bbox .. ';' .. remark .. '\n'
	end
	if primitives:find("w") then
		-- Include ways
		overpassUrl = overpassUrl .. 'way' .. leftbracket .. target .. itemID .. rightbracket .. query .. bbox .. ';\n'
	end
	if primitives:find("r") then
		-- Include relations
		overpassUrl = overpassUrl .. 'relation' .. leftbracket .. target .. itemID .. rightbracket .. query .. bbox .. ';\n>>;\n'
	end	
	overpassUrl = overpassUrl .. ');\n'
	overpassUrl = overpassUrl .. 'out geom;\n'
	if args.style then
			style = args.style
	else
		if args.logo then
			logo = "  icon-image: url(" .. args.logo .. ');\n'
		else
			if itemObject then
				logo = tostring(itemObject:formatPropertyValues('P154')['value']):gsub("'", "'")
			end
			if logo and not(logo == '') then
				logo = '  icon-image: url("https://commons.wikimedia.org/wiki/Special:Redirect/file/'.. logo .. '");\n'
			end
		end
		if logo then
			style = "node [".. target .."]{\n  text: name;\n".. logo .. "  icon-width: 32;}"
		end
	end
	if style then
		overpassUrl = overpassUrl .. '{{style:\n' .. style .. '\n}}\n'
	end

	return leftLink .. 'http://overpass-turbo.eu/?Q=' .. mw.uri.encode(overpassUrl, "PATH" ) .. coord .. runQueryRightAway .. textToDisplay .. rightLink
end

local p = {}

function p.overpass( frame )
	frame.args.overpass = true
	return buildquery(frame, '')
end

function p.wd( frame )
	return buildquery(frame, 'wikidata')
end

function p.pt( frame )
 	frame.args.style = "node {\n  opacity: 0;\n  fill-opacity: 0;}\nnode[highway=bus_stop], way[highway=bus_stop]{\n  text: name;\n  icon-image: url('icons/maki/bus-18.png');\n  icon-width: 18;}"
 	frame.args.prim = "r"
 	frame.args.timeout = 50
	return buildquery(frame, 'wikidata')
end

function p.etym( frame )
	return buildquery(frame, 'name:etymology:wikidata')
end

function p.subject( frame )
	return buildquery(frame, 'subject:wikidata')
end

function p.artist( frame )
	return buildquery(frame, 'artist:wikidata')
end

function p.architect( frame )
	return buildquery(frame, 'architect:wikidata')
end

function p.operator( frame )
	return buildquery(frame, 'operator:wikidata')
end

function p.brand( frame )
	return buildquery(frame, 'brand:wikidata')
end

return p