OpenHistoricalMap/QLever

From OpenStreetMap Wiki
Jump to navigation Jump to search

QLever provides an endpoint for rapidly querying the global OpenHistoricalMap dataset. As with the Overpass API, you can query for elements matching specific criteria; however, you use the standard query language SPARQL. SPARQL queries can federate with a number of other datasets, including OpenStreetMap and Wikidata.

Usage

Most people use QLever through an interactive Web application that provides syntax highlighting for the OverpassQL language and name-based autocompletion for OHM elements and tags. QLever can display results as a table, map, or heatmap using the Petrimaps service. It can also export the results as CSV, TSV, or GeoJSON. The Web application is located at:

https://qlever.cs.uni-freiburg.de/ohm-planet/

For raw queries from desktop applications, QLever's API endpoint for the OHM Planet dataset is located at:

https://qlever.cs.uni-freiburg.de/api/ohm-planet

Prefixes

Individual OpenHistoricalMap elements are stored under an ohmnode:, ohmway:, or ohmrel: prefix, corresponding to the elements' URLs on OHM. This makes it possible to distinguish between OHM and OSM elements in a federated query. However, other predicates are under the same OSM prefixes as they are in the OSM Planet dataset, and the objects of the rdf:type predicate are also under the osm: prefix, as OHM shares these concepts with OSM.

Prefix URL Description
ohmnode: https://www.openhistoricalmap.org/node/ Node IDs
ohmway: https://www.openhistoricalmap.org/way/ Way IDs
ohmrel: https://www.openhistoricalmap.org/relation/ Relation IDs
osmkey: https://www.openstreetmap.org/wiki/Key: Keys (including OHM-specific keys)
osmmeta: https://www.openstreetmap.org/meta/ Element metakeys such as osmmeta:timestamp
osmrel: https://www.openstreetmap.org/relation/ Relation metakeys such as osmrel:member (not for relation IDs)
osm2rdf: https://osm2rdf.cs.uni-freiburg.de/rdf# Metakeys added by osm2rdf such as osm2rdf:facts
osm2rdfkey: https://osm2rdf.cs.uni-freiburg.de/rdf/key# Keys reinterpreted as RDF types by osm2rdf, such as osm2rdfkey:wikidata (as wd:) and osm2rdfkey:start_date (as xsd:date, xsd:month, or xsd:year)
osm2rdfmember: https://osm2rdf.cs.uni-freiburg.de/rdf/member# Relation member metakeys such as osm2rdfmember:role and osm2rdfmember:id

Query examples

Main article: QLever#Examples

OpenHistoricalMap's data model allows you to analyze geographical data in a historical context, as well as real-world changes over time, not just data on the present as with the OSM Planet dataset.

Theaters in a given year

This query returns all the theaters (amenity=theatre) that existed in the year 1975. It relies on the fact that you can generally sort ISO 8601 dates chronologically by sorting them alphanumerically.

PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT ?theater ?start_date ?end_date ?geometry WHERE {
  ?theater osmkey:amenity "theatre" ;
		   osmkey:start_date ?start_date ;
		   geo:hasGeometry/geo:asWKT ?geometry .
  OPTIONAL {
    ?theater osmkey:end_date ?end_date .
  }
  # 1975-01-01 sorts after 1975 but before 1976.
  FILTER(?start_date < "1976" && COALESCE(?end_date, "9999") >= "1975")
}

Run it (edit query)


Oldest buildings in a present-day region

This query returns the ten oldest mapped buildings in Ohio; that is, the buildings with the minimum start_date=* tags:

PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX ohmrel: <https://www.openhistoricalmap.org/relation/>
PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT * WHERE {
  # Present-day Ohio
  ohmrel:2663622 ogc:sfContains ?building .
  ?building osmkey:building ?building_type ;
		    osmkey:start_date ?start_date ;
		    geo:hasGeometry/geo:asWKT ?geometry .
}
ORDER BY ?start_date
LIMIT 10

Run it (edit query)


Buildings built in one region but now in another region

This query returns the buildings that stood in Virginia at the time of construction but now stand in a different state because Virginia is much smaller than it used to be.

PREFIX osm2rdfmember: <https://osm2rdf.cs.uni-freiburg.de/rdf/member#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX ohmrel: <https://www.openhistoricalmap.org/relation/>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
SELECT
  ?building
  (SAMPLE(?boundary) AS ?a_boundary)
  (MIN(?boundary_start_date) AS ?min_boundary_start_date)
  (SAMPLE(?boundary_end_date) AS ?a_boundary_end_date)
  (SAMPLE(?building_start_date) AS ?a_building_start_date)
  (SAMPLE(?geometry) AS ?a_geometry)
WHERE {
  # Territorial evolution of Virginia
  ohmrel:2697886 osmrel:member/osm2rdfmember:id ?boundary .
  # Past boundaries of Virginia all have end dates.
  ?boundary osmkey:start_date ?boundary_start_date ;
			osmkey:end_date ?boundary_end_date .
  ?boundary ogc:sfContains ?building .
  ?building osmkey:building ?building_type ;
		    osmkey:start_date ?building_start_date ;
		    geo:hasGeometry/geo:asWKT ?geometry .
  FILTER(?building_start_date >= ?boundary_start_date && ?building_start_date <= ?boundary_end_date)
  MINUS {
	# Present-day Virginia
	ohmrel:2693701 ogc:sfContains ?building .
  }
}
GROUP BY ?building

Run it (edit query)


Shops that have moved the farthest

This query returns the 10 shops that have moved the farthest over time. Each shop represented by a chronology relation is ranked by the sum of the distances between each location it has occupied throughout its history, not by the straight-line distance between its original and final locations. This query is limited to nodes, because QLever currently only supports computing the distance between two points. [1] Fortunately, shops tend to be mapped as nodes rather than as areas.

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX osm: <https://www.openstreetmap.org/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
PREFIX osm2rdfmember: <https://osm2rdf.cs.uni-freiburg.de/rdf/member#>
SELECT ?chronology (SUM(?distance) AS ?total_distance) WHERE {
  ?chronology osmkey:type "chronology" .
  ?chronology osmrel:member ?older_member .
  ?chronology osmrel:member ?newer_member .
  ?older_member osm2rdfmember:pos ?older_pos ;
				osm2rdfmember:id ?older_poi .
  ?newer_member osm2rdfmember:pos ?newer_pos ;
				osm2rdfmember:id ?newer_poi .
  FILTER(?older_pos + 1 = ?newer_pos)
  ?older_poi rdf:type osm:node ;
			 osmkey:shop ?older_shop_type .
  ?newer_poi rdf:type osm:node ;
			 osmkey:shop ?newer_shop_type .
  ?older_poi geo:hasGeometry/geo:asWKT ?older_geometry .
  ?newer_poi geo:hasGeometry/geo:asWKT ?newer_geometry .
  BIND(geof:distance(?older_geometry, ?newer_geometry) AS ?distance)
}
GROUP BY ?chronology
ORDER BY DESC(?total_distance)
LIMIT 10

Run it (edit query)


Maximum territorial extent of an empire

This query determines when the Ottoman Empire reached its greatest land area:

PREFIX osm2rdfmember: <https://osm2rdf.cs.uni-freiburg.de/rdf/member#>
PREFIX ohmrel: <https://www.openhistoricalmap.org/relation/>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
PREFIX osm2rdf: <https://osm2rdf.cs.uni-freiburg.de/rdf#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
SELECT * WHERE {
  # Territorial evolution of the Ottoman Empire
  ohmrel:2747251 osmrel:member/osm2rdfmember:id ?boundary .
  ?boundary osm2rdf:area ?area ;
			osmkey:start_date ?start_date ;
			osmkey:end_date ?end_date .
}
ORDER BY DESC(?area)
LIMIT 1

Run it (edit query)


Total railway distance in a given year

This query returns the sum total distance of railroad tracks that existed in the year 1975 in kilometers:

PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX osm2rdf: <https://osm2rdf.cs.uni-freiburg.de/rdf#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX osm: <https://www.openstreetmap.org/>
SELECT (SUM(?length*100) AS ?total_length) WHERE {
  ?railway rdf:type osm:way ;
		   osmkey:railway ?railway_type ;
		   osmkey:start_date ?start_date ;
		   osm2rdf:length ?length .
  OPTIONAL {
    ?railway osmkey:end_date ?end_date .
  }
  # 1975-01-01 sorts after 1975 but before 1976.
  FILTER(?start_date < "1976" && COALESCE(?end_date, "9999") >= "1975")
}

Run it (edit query)


Number of buildings in a region by year

This query returns the total number of mapped buildings that stood within present-day Virginia in every year from 1600 CE to present. It uses VALUES statements to generate the digits of each year.

PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX ohmrel: <https://www.openhistoricalmap.org/relation/>
PREFIX ogc: <http://www.opengis.net/rdf#>
SELECT ?year (COUNT(?building) AS ?buildings) WHERE {
  VALUES ?ones { 0 1 2 3 4 5 6 7 8 9 }
  VALUES ?tens { 0 1 2 3 4 5 6 7 8 9 }
  VALUES ?hundreds { 0 1 2 3 4 5 6 7 8 9 }
  VALUES ?thousands { 1 2 }
  BIND((?thousands * 1000 + ?hundreds * 100 + ?tens * 10 + ?ones) AS ?year)
  FILTER(?year >= 1600 && ?year <= 2024)
  # Present-day Virginia
  ohmrel:2693701 ogc:sfContains ?building .
  ?building osmkey:building ?building_type ;
		    osmkey:start_date ?start_date .
  OPTIONAL {
	?building osmkey:end_date ?end_date .
  }
  # 1975-01-01 sorts after 1975 but before 1976.
  FILTER(STR(?start_date) < STR(?year + 1) && COALESCE(STR(?end_date), "9999") >= STR(?year))
}
GROUP BY ?year
ORDER BY ?year

Run it (edit query)


Contemporaneous surrounding areas

When recounting a historical event or someone's biography, you typically need to qualify a place with the country, region, etc. that the place was in at the time, not where it's located in the present day. This query returns the boundaries that contained Jura when it was still a town:

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX geof: <http://www.opengis.net/def/function/geosparql/>
PREFIX ohmnode: <https://www.openhistoricalmap.org/node/>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX osm2rdf: <https://osm2rdf.cs.uni-freiburg.de/rdf#>
SELECT ?boundary ?name ?boundary_start_date ?boundary_end_date WHERE {
  # Jena town
  ohmnode:2091211419 ogc:sfIntersects ?boundary ;
					 osmkey:start_date ?place_start_date ;
					 osmkey:end_date ?place_end_date .
  ?boundary osmkey:start_date ?boundary_start_date ;
			osmkey:name ?name ;
			osm2rdf:area ?area .
  OPTIONAL {
    ?boundary osmkey:end_date ?boundary_end_date .
  }
  FILTER(?place_start_date >= ?boundary_start_date && ?place_end_date <= COALESCE(?boundary_end_date, "9999"))
}
ORDER BY ?area

Run it (edit query)


Changes on this day in history

This query finds features that began or ended on the current day in years past. The OpenHistoricalMap portal's "On this day in OpenHistoricalMap" feature curates some notable and interesting changes from this query:

PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT ?element ?name ?start_date ?end_date ?geometry WHERE {
  {
    ?element osmkey:start_date ?start_date .
	FILTER(STRENDS(?start_date, "-04-28"))
#	or: FILTER(MONTH(?start_date) = 4 && DAY(?start_date) = 23)
  } UNION {
    ?element osmkey:end_date ?end_date .
	FILTER(STRENDS(?end_date, "-04-28"))
#	or: FILTER(MONTH(?end_date) = 4 && DAY(?end_date) = 23)
  }
  OPTIONAL {
	?element osmkey:name ?name .
  }
  OPTIONAL {
	?element geo:hasGeometry/geo:asWKT ?geometry .
  }
}

Run it (edit query)


Unsorted chronologies

A type=chronology relation's members are supposed to be sorted in chronological order, and ideally no member overlaps another member's date range. This query finds chronologies that appear to be unsorted or have chronologically overlapping members:

PREFIX osm2rdfmember: <https://osm2rdf.cs.uni-freiburg.de/rdf/member#>
PREFIX osm2rdf: <https://osm2rdf.cs.uni-freiburg.de/rdf#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX osm: <https://www.openstreetmap.org/>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
SELECT * WHERE {
  ?chronology rdf:type osm:relation ;
			  osmkey:type "chronology" ;
			  osmrel:member ?older ;
			  osmrel:member ?newer .
  ?older osm2rdfmember:pos ?older_pos .
  ?newer osm2rdfmember:pos ?newer_pos .
  FILTER(?older_pos + 1 = ?newer_pos)
  ?older osm2rdfmember:id/osmkey:start_date ?older_start ;
		 osm2rdfmember:id/osmkey:end_date ?older_end .
  ?newer osm2rdfmember:id/osmkey:start_date ?newer_start ;
		 osm2rdfmember:id/osmkey:end_date ?newer_end .
  FILTER(?older_start > ?newer_start

Run it (edit query)


Federated query examples

Demolished buildings that are safe to delete from OpenStreetMap based on Wikidata

This query returns buildings that have been demolished but remain in OpenStreetMap as demolished:building=* areas despite already having been mapped in OpenHistoricalMap for safekeeping. The query matches OHM and OSM buildings based on their wikidata=* tags.

PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX osm2rdfkey: <https://osm2rdf.cs.uni-freiburg.de/rdf/key#>
SELECT ?osm_building ?item ?ohm_building ?end_date WHERE {
  ?ohm_building osmkey:building ?ohm_building_type .
  ?ohm_building osm2rdfkey:wikidata ?item .
  SERVICE <https://qlever.cs.uni-freiburg.de/api/osm-planet> {
	?osm_building osmkey:demolished:building ?osm_building_type .
	?osm_building osm2rdfkey:wikidata ?item .
  }
  OPTIONAL {
	?ohm_building osmkey:end_date ?end_date .
  }
}

Run it (edit query)


Member states of a defunct international organization

This query returns the contemporary boundaries of the members of the League of Nations based on Wikidata. Similar queries can help improve thematic maps that often feature anachronistic boundaries. However, as of January 2024, this query returns very few of the member states, due to a combination of factors in including broken boundary relations and missing wikidata=* tags.

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osm: <https://www.openstreetmap.org/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX wd: <http://www.wikidata.org/entity/>
PREFIX wdt: <http://www.wikidata.org/prop/direct/>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
PREFIX osm2rdfkey: <https://osm2rdf.cs.uni-freiburg.de/rdf/key#>
SELECT DISTINCT ?boundary ?name ?item (STR(?boundary_start) AS ?start) (STR(?boundary_end) AS ?end) ?geometry WHERE {
  SERVICE <https://qlever.cs.uni-freiburg.de/api/wikidata> {
	?item (^wdt:P17)?/wdt:P463 wd:Q38130 .
	wd:Q152283 wdt:P571 ?league_start ;
			   wdt:P576 ?league_end .
  }
  ?boundary rdf:type osm:relation ;
			osmkey:type "boundary" ;
			osmkey:name ?name ;
			osmkey:start_date ?boundary_start ;
			geo:hasGeometry/geo:asWKT ?geometry .
  {
    ?boundary osm2rdfkey:wikidata ?item .
  } UNION {
	?chronology rdf:type osm:relation ;
				osmkey:type "chronology" ;
				osm2rdfkey:wikidata ?item ;
				osmrel:member/osm:id ?boundary .
  }
  OPTIONAL {
    ?boundary osmkey:end_date ?boundary_end .
  }
  FILTER(STR(?boundary_start) < STR(?league_end) &&
  	     STR(COALESCE(?boundary_end, "9999")) >= STR(?league_start))
}

Run it (edit query)