Workshop 1: Kaartenproductie in de zeventiende eeuw

Amsterdam was in de zeventiende eeuw het belangrijkste productiecentrum van geografische kaarten ter wereld. Blaeu en Janssonius zijn nog altijd bekende namen, ook buiten de kring van specialisten op het gebied van de historische cartografie. Veel van wat destijds is geproduceerd, is terug te vinden in de cartografische collectie van de Bibliotheek UvA/HvA. In deze sessie verbinden we de metadata van deze collectie met de aan de UvA ontwikkelde Ecartico database waarin data zijn te vinden over producenten van kaarten en hun sociale netwerken. Zo hopen we meer inzicht te krijgen in hoe samenwerking en concurrentie in de zeventiende-eeuwse cartografie gestalte kregen. Vervolgens bekijken we of het mogelijk en zinvol is om andere data aan dit amalgaam toe te voegen. Denk bijvoorbeeld aan de collectiedatabase van het Rijksmuseum, of de open data van de Koninklijke Bibliotheek.

Feedback?

Tijdens de workshop werken we met een eerste vroege versie van de LOD van de UB. Heb je foutjes gezien die verbeterd moeten worden? Moet het datamodel uitgebreid worden, of heb je andere opmerkingen? Laat het ons weten via een gedeeld online document. Dit is bedoeld als feedback op de data. Resultaten en query’s kunnen op deze pagina’s gepresenteerd worden!

Startpunt: statistieken

Laten we beginnen met het opvragen van wat statistieken in de Maps dataset van de UB en verwante datasets. Als startpunt van elke query op de UB-data kunnen we de volgende SPARQL-query gebruiken, waarmee we filteren op enkel werken uit de Maps collectie:

#+ endpoint: https://api.lod.uba.uva.nl/datasets/UB-UVA/Catalogue/services/virtuoso/sparql
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema: <http://schema.org/>

SELECT * WHERE {
  ?map a schema:Map .
}

Voer de query uit

Aantallen

Hoeveel kaarten zitten er bijvoorbeeld in deze collectie? Dat doen we met een COUNT statement in de query:

#+ endpoint: https://api.lod.uba.uva.nl/datasets/UB-UVA/Catalogue/services/virtuoso/sparql
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema: <http://schema.org/>

SELECT (COUNT(?map) AS ?aantal_maps) WHERE {
  ?map a schema:Map .
}

We krijgen een totaal van 26.866 terug.

Voer de query uit

Aantallen per jaar

We kunnen dit ook nog uitsplitsen in aantallen kaarten per jaar. Daarvoor hebben we echter wel een valide datumnotatie nodig, of in ieder geval iets consistents om op te groeperen. De collectie van de UB gebruikt o.a. de dc:date property voor de datum van de kaart en voor deze property kan je (helaas) vanalles invullen dat niet door een computer als datum herkend wordt. Er zijn ook sem:hasBeginTimeStamp en sem:hasEndTimeStamp properties die we kunnen gebruiken om de datum te bepalen, maar die hebben vooralsnog hetzelfde probleem dat een datum daarin niet noodzakelijkerwijs valide is in deze dataset.

De oplossing voor nu is om een filter in te stellen die ons alleen dit soort valide datumnotaties teruggeeft en dat kunnen we doen met een reguliere expressie (regex):

#+ endpoint: https://api.lod.uba.uva.nl/datasets/UB-UVA/Catalogue/services/virtuoso/sparql
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX schema: <http://schema.org/>

SELECT ?datum (COUNT(?kaart) AS ?kaarten) WHERE {
  ?kaart a schema:Map .  
  ?kaart dc:date ?datum .
  
  FILTER(REGEX(?datum, "^\\d{4}$")) 
  
} GROUP BY ?datum ORDER BY ?datum

Het resultaat is een lijst met de datum als jaar met daarnaast het aantal kaarten uit dit jaar. De tabel is te groot om hier weer te geven, maar we kunnen dit wel visualiseren als een grafiek met staafdiagrammen: Aantal kaarten over tijd

Voer de query uit

Verbinden met een andere dataset: ECARTICO

Dit geeft ons al een idee van de inhoud van de dataset. Het zwaartepunt zit duidelijk op de twintigste eeuw, maar de mooiste kaarten komen natuurlijk uit de zestiende en zeventiende eeuw.

Laten we proberen om deze dataset, volgens de idee van Linked Data, te verbinden met een andere dataset. In dit geval willen we meer weten over de makers/bijdragers van de kaarten van vóór 1740 (eindpunt van de ‘lange Gouden Eeuw’). In de dataset van de UB zijn zij volgens de Dublin Core vocabulaire opgenomen als waarde van een dc:creator en dc:contributor property. Een voorbeeld van Germania van Abraham Ortelius, waar hij is aangeduid met zijn Nederlandse Thesaurus Auteursnamen URI:

@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix edm: <http://www.europeana.eu/schemas/edm/> .
@prefix schema: <http://schema.org/> .
@prefix sem: <http://semanticweb.cs.vu.nl/2009/11/sem/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://pid.uba.uva.nl/ark:/88238/b1990013246490205131>
  a schema:Map, edm:ProvidedCHO ;
  rdfs:label "Germania" ;
  dc:contributor <http://data.bibliotheken.nl/id/thes/p069075301> ;
  dc:date "1570",
      "[1570]" ;
  dc:description "Cum privilegio",
      "De eerste koperplaat die tot 1603 gebruikt werd",
      "Handgekleurd",
      "Staatverschillen: Van den Broecke 56.1 (naast Moesel fl.): Bergcastel; 56.2: Bergtastel",
      "Tekst verso: Alemaigne; signatuur: 29",
      "Tekst verso: Alemaigne; signatuur: 36",
      "Tekst verso: Germania; signatuur 29",
      "Tekst verso: Germania; signatuur: 31",
      "Tekst verso: Germania; signatuur: 33",
      "Van den Broecke staat 56.2",
      "Van den Broecke: staat 56.2",
      "Verso blanco" ;
  dc:identifier "Call Nr:OTM: HB-KZL 31.20.24OTM: HB-KZL 31.20.23OTM: HB-KZL 31.20.19OTM: HB-KZL 31.20.18OTM: HB-KZL 31.20.20OTM: HB-KZL 31.20.22OTM: HB-KZL 31.20.21",
      "OCLC:71530099",
      "PICA:171717635",
      "ark:https://pid.uba.uva.nl/ark:/88238/b1990013246490205131" ;
  dc:language "lat" ;
  dc:publisher "[Antwerpen] [Abraham Ortelius]" ;
  dc:relation "Ook in: Theatrum Orbis Terrarum" ;
  dc:subject <http://data.bibliotheken.nl/id/thes/p077610970> ;
  dc:title "Germania" ;
  dc:type <http://vocab.getty.edu/aat/300028094>,
      "map" ;
  dcterms:extent "1 krt kopergrav 35 x 48,5 cm, met kader 36,5 x 50,5 cm" ;
  dcterms:isReferencedBy "Karrow, Mapmakers sixteenth century 1/21; Koeman, Atlantes Neerlandici Ort 1A-1D[13]; Meurer, Fontes 13; Werner, Abraham Ortelius, no. 29",
      "Van den Broecke (Ortelius atlas maps. 2nd., rev. ed.), Ort56" ;
  dcterms:spatial <http://data.bibliotheken.nl/id/thes/p078448689>,
      <http://data.bibliotheken.nl/id/thes/p07849043X>,
      <http://data.bibliotheken.nl/id/thes/p078613884>,
      <http://data.bibliotheken.nl/id/thes/p078630797>,
      <http://data.bibliotheken.nl/id/thes/p078971136>,
      <http://data.bibliotheken.nl/id/thes/p107115816>,
      "Schaal [ca. 1:3.000.000]" ;
  sem:hasBeginTimeStamp "1570" ;
  sem:hasEndTimeStamp "    " ;
  edm:currentLocation "Allard Pierson Handbibliotheek" ;
  owl:sameAs <http://data.bibliotheken.nl/id/nbt/p171717635>,
      <http://www.worldcat.org/oclc/71530099> .

<http://data.bibliotheken.nl/id/thes/p069075301> a schema:Person, edm:Agent ;
  schema:name "Ortelius, Abraham," ;
  schema:birthDate "1527" ;
  schema:deathDate "1598" .

Bovenstaande code is een representatie of ‘serialisatie’ van Linked Data in het Turtle RDF formaat. Je kunt hier de triples in herkennen, met wat afkortingen en speciale syntax die de code leesbaarder maken. Dit zijn tevens alle triples die je op kunt vragen voor de kaart met URI https://pid.uba.uva.nl/ark:/88238/b1990013246490205131. De URI van Ortelius is http://data.bibliotheken.nl/id/thes/p069075301. Meer over informatie over het turtle-formaat formaat: https://www.w3.org/TR/turtle/

Zoals je ziet, is de informatie die beschikbaar is in het endpoint van de UB over Ortelius beperkt, want alleen zijn naam en geboorte- en sterfjaar zijn opgenomen. Voor meer informatie over hem, zullen we dus te rade moeten gaan bij een andere dataset. ECARTICO bijvoorbeeld.

ECARTICO bevat veel meer biografische informatie over Ortelius. Zo weten we op welke dag en waar hij geboren en overleden is, en waar hij heeft gewerkt. Omdat ECARTICO ook beschikbaar is als Linked Open Data, kunnen we ook alle triples over Ortelius uit deze dataset halen:

@prefix schema: <http://schema.org/> .
@prefix geo: <http://www.opengis.net/ont/geosparql#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix pnv: <https://w3id.org/pnv#> .
@prefix ecartico: <http://www.vondel.humanities.uva.nl/ecartico/lod/vocab/#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .

<https://www.vondel.humanities.uva.nl/ecartico/persons/5797>
  a schema:Person ;
  schema:name "Abraham Ortelius" ;
  pnv:hasName <https://www.vondel.humanities.uva.nl/ecartico/persons/5797#names1> ;
  schema:gender schema:Male ;
  schema:birthPlace <https://www.vondel.humanities.uva.nl/ecartico/places/13> ;
  schema:birthDate "1527-04-14"^^xsd:date ;
  schema:deathPlace <https://www.vondel.humanities.uva.nl/ecartico/places/13> ;
  schema:deathDate "1598-06-29"^^xsd:date ;
  schema:parent <https://www.vondel.humanities.uva.nl/ecartico/persons/23776>, <https://www.vondel.humanities.uva.nl/ecartico/persons/23777> ;
  schema:hasOccupation <https://www.vondel.humanities.uva.nl/ecartico/persons/5797#jt8626> ;
  schema:workLocation <https://www.vondel.humanities.uva.nl/ecartico/persons/5797#wl5871> ;
  ecartico:religion "Roman Catholic"@en ;
  owl:sameAs <http://data.bibliotheken.nl/id/dbnla/orte001>, <http://data.bibliotheken.nl/id/thes/p069075301>, <http://ta.sandrart.net/-person-2146>, <http://viaf.org/viaf/32104723>, <http://vocab.getty.edu/ulan/500011462>, <http://www.biografischportaal.nl/persoon/06002613>, <http://www.biografischportaal.nl/persoon/32239840>, <http://www.biografischportaal.nl/persoon/79731241>, <http://www.wikidata.org/entity/Q232916>, <https://id.rijksmuseum.nl/31086307>, <https://rkd.nl/explore/artists/60986>, <urn:rijksmuseum:people:RM0001.PEOPLE.81106> .

<https://www.vondel.humanities.uva.nl/ecartico/persons/5797#names1>
  a pnv:PersonName ;
  pnv:givenName "Abraham" ;
  pnv:baseSurname "Ortelius" .

<https://www.vondel.humanities.uva.nl/ecartico/persons/5797#jt8626>
  a schema:Role ;
  schema:hasOccupation <https://www.vondel.humanities.uva.nl/ecartico/occupations/40> .

<https://www.vondel.humanities.uva.nl/ecartico/persons/5797#wl5871>
  a schema:Role ;
  schema:workLocation <https://www.vondel.humanities.uva.nl/ecartico/places/129> ;
  schema:startDate "1560"^^xsd:gYear ;
  schema:endDate "1560"^^xsd:gYear .

<https://www.vondel.humanities.uva.nl/ecartico/occupations/40>
  a schema:Occupation ;
  rdfs:label "Kartograf"@de, "cartographer"@en, "cartographe"@fr, "cartograaf"@nl ;
  schema:name "Kartograf"@de, "cartographer"@en, "cartographe"@fr, "cartograaf"@nl ;
  skos:prefLabel "Kartograf"@de, "cartographer"@en, "cartographe"@fr, "cartograaf"@nl ;
  owl:sameAs <http://www.wikidata.org/entity/Q1734662> .

<https://www.vondel.humanities.uva.nl/ecartico/places/129>
  a schema:Place ;
  schema:name "Duitsland" ;
  schema:geo <https://www.vondel.humanities.uva.nl/ecartico/places/129#geo> ;
  geo:hasGeometry <https://www.vondel.humanities.uva.nl/ecartico/places/129#thisGeometry> ;
  owl:sameAs <http://www.wikidata.org/entity/Q183>, <http://sws.geonames.org/2921044/>, <http://vocab.getty.edu/tgn/7000084>, <https://rkd.nl/explore/thesaurus?term=456>, <http://data.bibliotheken.nl/id/thes/p075596776> .

<https://www.vondel.humanities.uva.nl/ecartico/places/129#geo>
  a schema:GeoCoordinates ;
  schema:latitude 51.5 ;
  schema:longitude 10.5 .

<https://www.vondel.humanities.uva.nl/ecartico/places/129#thisGeometry>
  a <http://www.opengis.net/ont/sf#Point> ;
  geo:asWKT "POINT(10.5 51.5)"^^geo:wktLiteral .

Bovenstaande RDF is een ingekorte versie (t.b.v. de leesbaarheid) van de werkelijke RDF waar alle werklocaties en beroepen van Ortelius in vermeld zijn.

Maar, hoe komen we nu van de UB-data naar de data uit ECARTICO? Er is geen directe link tussen de twee datasets aanwezig (i.e. ze verwijzen niet naar elkaar), maar ze verwijzen wel naar een gezamenlijke URI: de NTA-URI van Ortelius (http://data.bibliotheken.nl/id/thes/p069075301). De UB-data doet dat door deze URI te gebruiken in hun data. ECARTICO heeft deze URI opgenomen in een owl:sameAs-link.

Schematisch kunnen we nu visualiseren hoe de resources in onze twee datasets met elkaar verbonden zijn. De verwijzing naar de NTA-URI is wat hen verbindt.

graph BT
UB(UvA Map) -- dc:contributor --> NTA(NTA Person)

ECARTICO(ECARTICO Person) -- owl:sameAs ---> NTA

ECARTICO -- owl:sameAs ---> WD(Wikidata)

ECARTICO -- schema:hasOccupation --> Beroep(Occupation)
ECARTICO -- schema:workLocation --> Werklocatie(Place)

Data visualiseren

Nu we vanuit de UB-data toegang hebben tot biografische data uit ECARTICO kunnen we een poging doen om de data te visualiseren, bijvoorbeeld op een kaartje. We zijn geïnteresseerd in de werklocaties van alle cartografen van kaarten uit de UB-collectie tot 1740. Per locatie willen we een overzicht van hoeveel cartografen er op die locatie werkzaam zijn.

We beginnen onze query met een vraag naar alle cartografen die in aanmerking komen, omdat zij meegewerkt hebben aan een kaart gepubliceerd voor 1740. We kunnen alleen deze gegevens opvragen als er een NTA-link beschikbaar is, dus we filteren ook op een data.bibliotheken.nl URI. Helaas missen we daardoor de bijdragers die als tekst opgenomen zijn. De (sub-)query hiervoor:

SELECT DISTINCT ?cartograafNTA WHERE {
    ?kaart a schema:Map ;
           dc:date ?datum ;
           dc:creator|dc:contributor ?cartograafNTA . # zowel creator als contributor
  
    FILTER(REGEX(?datum, "((15|16)[0-9][0-9]|17[0-3])")) # Datums van 1500 t/m 1739

    FILTER(CONTAINS(STR(?cartograafNTA), 'data.bibliotheken')) # Alleen met koppeling naar de NTA
    
    }

Het resultaat hiervan is een lijst van NTA-URIs van cartografen die aan deze criteria voldoen. Voor hen vragen we biografische gegevens op uit ECARTICO in een gefedereerde subquery:

  {
    # Onderstaande wordt doorgestuurd naar het CREATE endpoint
    SERVICE <https://data.create.humanities.uva.nl/sparql> {
      GRAPH <https://data.create.humanities.uva.nl/id/ecartico/> {
        ?persoon a schema:Person ;
                 schema:name ?naam ;
                 schema:hasOccupation ?occupationRole ;         
                 schema:workLocation ?workLocationRole ;
                 owl:sameAs ?cartograafNTA . # dit is onze link/join
      
        ?occupationRole schema:hasOccupation <https://www.vondel.humanities.uva.nl/ecartico/occupations/40> . # cartograaf
      
        ?workLocationRole schema:workLocation ?werkLocatie .
      
        ?werkLocatie a schema:Place ;
                     schema:name ?pointTooltip ;
                     geo:hasGeometry/geo:asWKT ?point .
      
        FILTER(CONTAINS(STR(?cartograafNTA), 'data.bibliotheken')) # Alleen met koppeling naar de NTA
      
      
     } 
    }
  }

Vervolgens voegen we de twee samen in één grote query die de beide datasets tegelijk bevraagt. We gebruiken daarvoor ook speciale syntax in het SELECT veld, om de resultaten te kunnen visualiseren op een kaart: point, pointToolTip en pointLabel.

#+ endpoint: https://api.lod.uba.uva.nl/datasets/UB-UVA/Catalogue/services/virtuoso/sparql
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX schema: <http://schema.org/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>

SELECT DISTINCT (COUNT(?persoon) AS ?pointLabel) ?point ?pointTooltip WHERE {
  
  {
  SELECT DISTINCT ?cartograafNTA WHERE {
    ?kaart a schema:Map ;
           dc:date ?datum ;
           dc:creator|dc:contributor ?cartograafNTA . # zowel creator als contributor
  
    FILTER(REGEX(?datum, "((15|16)[0-9][0-9]|17[0-3])")) # Datums van 1500 t/m 1739

    FILTER(CONTAINS(STR(?cartograafNTA), 'data.bibliotheken')) # Alleen met koppeling naar de NTA
    }
  }
  
  {
    SERVICE <https://data.create.humanities.uva.nl/sparql> {
      GRAPH <https://data.create.humanities.uva.nl/id/ecartico/> {
        ?persoon a schema:Person ;
                 schema:name ?naam ;
                 schema:hasOccupation ?occupationRole ;         
                 schema:workLocation ?workLocationRole ;
                 owl:sameAs ?cartograafNTA . # dit is onze link/join
      
        ?occupationRole schema:hasOccupation <https://www.vondel.humanities.uva.nl/ecartico/occupations/40> . # cartograaf
      
        ?workLocationRole schema:workLocation ?werkLocatie .
      
        ?werkLocatie a schema:Place ;
                     schema:name ?pointTooltip ;
                     geo:hasGeometry/geo:asWKT ?point .
      
        FILTER(CONTAINS(STR(?cartograafNTA), 'data.bibliotheken')) # Alleen met koppeling naar de NTA
      
      
     } 
    }
  }
}


Voer deze query uit

Het resultaat daarvan kun je zien als je op bovenstaande link klikt. Een voorbeeld in een schermafdruk:

Kaart met daarop aantallen cartografen per plaats

Combineer cartografen met afbeeldingen uit wikidata

We kunnen nog een stap verder gaan en de data ook verbinden aan Wikidata. Hieruit kunnen we bijvoorbeeld een afbeelding (e.g. potret) van de cartograaf ophalen. Opnieuw is de schakel hier de NTA-URI, die ook in Wikidata opgenomen is via property P1006. We gebruiken ook hier speciale syntax die deze afbeeldingen ook toont in de viewer van de sparql-editor. Met dank aan Ruben Schalk voor deze query.

#+ endpoint: https://api.lod.uba.uva.nl/datasets/UB-UVA/Catalogue/services/virtuoso/sparql
SELECT distinct ?wikidata ?image ?creator ?widget WHERE {
  
  ?kaart a <http://schema.org/Map> ;
         dc:date ?date ;
         dc:creator|dc:contributor ?creator .
  
  FILTER(CONTAINS(STR(?creator), "data.bibliotheken"))
  
  SERVICE <https://query.wikidata.org/bigdata/namespace/wdq/sparql> {
    
    ?wikidata <http://www.wikidata.org/prop/direct-normalized/P1006> ?creator ;
              wdt:P18 ?image . # Afbeelding
    }

  BIND('''
    <h4><p>cartograaf: <a href="{{creator}}" target="blank">{{creator}}</a></p></h4>
    <a href="{{creator}}" target="blank"><img src="{{image}}" style="max-width:280px;"></a>
	     '''^^rdf:HTML as ?widget)
  
 # FILTER(REGEX(?date, "^\\d{4}$")).
 # FILTER(regex(?date, "((15|16)[0-9][0-9]|17[0-3])", "i" )) 
  
} LIMIT 10


Voer deze query uit

Klik op de link hierboven voor een portrettengalerij van 10 cartografen. Die moet er ongeveer zo uitzien:

Portrettengalerij van cartografen