MIST™

your low-level cloud provider™

a Royal Shitware Inc (RSI) service

Postcode / Kadaster lookup

How to use this page:

You can also use this page as an API in both JSON and XML format.

Data is refreshed every 10th of the month.

Resources:

Source to parse the data:

<?php

define
('AREA_SOURCE','https://www.cbs.nl/-/media/cbs/onze-diensten/methoden/classificaties/overig/gemeenten-alfabetisch-*.xlsx'); //replace asterisk with next or this year
define('ZIP_SOURCE','https://service.pdok.nl/kadaster/adressen/atom/v1_0/downloads/lvbag-extract-nl.zip');
define('SUB_PREFIX','9999');

$area_filename '/var/www/tmp/gemeenten-alfabetisch.xlsx';
$area_interval 28 86400;
$zip_filename '/var/www/tmp/lvbag-extract-nl.zip';
$zip_interval 86400;
$temp_path '/tmp/'//incl trailing /

if(!is_file($area_filename) || (filemtime($area_filename) < time() - $area_interval)){
  print(
"Downloading areas (gemeenten) file\n");
  if(!@
copy(str_replace('*',($year date('Y')) + 1,AREA_SOURCE),$area_filename)) copy(str_replace('*',$year,AREA_SOURCE),$area_filename);
}
if(!
is_file($zip_filename) || (filemtime($zip_filename) < time() - $zip_interval)){
  print(
"Downloading data file Basisregistratie Adressen en Gebouwen (BAG) from Kadaster - this may take a while\n");
  
copy(ZIP_SOURCE,$zip_filename);
}

function 
xml_array($node){
  
$values = [];
  foreach(
$node as $value$values[] = strval($value);
  return 
$values;
}

function 
parse_bag($sub,$tag,$active,$callback){
  global 
$zip_filename,$temp_path;

  print(
"Extracting '$sub' from data file\n");
  
$zip = new ZipArchive();
  
$zip->open($zip_filename);

  for(
$i 0$i $zip->numFiles$i++) if(strpos($name $zip->statIndex($i)['name'],$sub) === 0){
    print(
"- Full name: $name\n");
    
$count $bytes 0;
    
$zip->extractTo($temp_path,$name);
    
$zip->close();
    
$zip->open($sub_zip_filename $temp_path $name);
    for(
$i 0$i $zip->numFiles$i++){
      
$zip->extractTo($temp_path,$name $zip->statIndex($i)['name']);
      print(
"- Processing $name\r");
      
$f fopen($data_filename $temp_path $name,'r');
      
$buffer null;
      while(!
feof($f)){
        
$buffer .= fread($f,102400);
        while(
$e strpos($buffer,"</$tag>")){ //end tag found
          
$s strpos($buffer,"<$tag>"); //start of start tag
          
$e += strlen($tag) + 3//end of end tag
          
$xml simplexml_load_string(preg_replace('/(<\\/?)[\\-\\w]+:/','$1',substr($buffer,$s,$e $s))); //remove all namespaces
          
if($xml->status == $activecall_user_func($callback,$xml);
          
$buffer substr($buffer,$e); //remove node from buffer
        
}
      }
      
fclose($f);
      
$count++;
      
$bytes += filesize($data_filename);
      
unlink($data_filename);
    }
    
$zip->close();
    
unlink($sub_zip_filename);
    print(
"Total " number_format($count) . " files, " number_format($bytes 1024 1024,2) . " Mb   \n");
    return 
true;
  }
  
$zip->close();
  die(
">> sub-ZIP not found\n");
}

$provinces $areas $places $spaces $numbers = []; //provincie, gemeente, plaatsen, ruimten (m.n. wegen), huisnummers (incl. postcode)

print("Processing area file ...\n");
$zip = new ZipArchive();
$zip->open($area_filename); //XLSX file = ZIP file with XML files
$xml simplexml_load_string($zip->getFromName('xl/sharedStrings.xml'));
$strings = [];
foreach(
$xml->si as $value$strings[] = strval($value->t);
$xml simplexml_load_string($zip->getFromName('xl/worksheets/sheet2.xml'));
$zip->close();
foreach(
$xml->sheetData->row as $row){
  if(!
array_key_exists($province $strings[strval($row->c[3]->v)],$provinces)) $provinces[$province] = ['name' => $strings[strval($row->c[5]->v)]];
  
$areas[$strings[strval($row->c[0]->v)]] = [
    
'name' => $strings[strval($row->c[2]->v)],
    
'province' => $province
  
];
}

parse_bag(SUB_PREFIX 'WPL','Objecten:Woonplaats','Woonplaats aangewezen',function($xml) use (&$places){
  
$places[strval($xml->identificatie)] = [
    
'name' => strval($xml->naam),
    
'areas' => []
  ];
});

parse_bag('GEM-WPL-RELATIE','gwr-product:GemeenteWoonplaatsRelatie','definitief',function($xml) use (&$places){
  
$places[strval($xml->gerelateerdeWoonplaats->identificatie)]['areas'][] = [
    
'area' => strval($xml->gerelateerdeGemeente->identificatie),
    
'start' => strval($xml->tijdvakgeldigheid->begindatumTijdvakGeldigheid),
    
'end' => strval($xml->tijdvakgeldigheid->einddatumTijdvakGeldigheid ?? null) ?: null,
  ];
});

parse_bag(SUB_PREFIX 'OPR','Objecten:OpenbareRuimte','Naamgeving uitgegeven',function($xml) use (&$spaces){
  
$spaces[strval($xml->identificatie)] = [
    
'name' => strval($xml->naam),
    
'short' => strval($xml->verkorteNaam->VerkorteNaamOpenbareRuimte->verkorteNaam ?? null) ?: null,
    
'type' => strval($xml->type), //Weg,Water,Landschappelijk gebied,Terrein,Kunstwerk,Administratief gebied,Spoorbaan
    
'places' => xml_array($xml->ligtIn->WoonplaatsRef)
  ];
});

parse_bag(SUB_PREFIX 'NUM','Objecten:Nummeraanduiding','Naamgeving uitgegeven',function($xml) use (&$numbers){
  
$numbers[$id strval($xml->identificatie)] = [
    
'number' => strval($xml->huisnummer),
    
'char' => strval($xml->huisletter ?? null) ?: null,
    
'suffix' => strval($xml->huisnummertoevoeging ?? null) ?: null,
    
'postcodes' => xml_array($xml->postcode ?? []),
    
'start' => strval($xml->voorkomen->Voorkomen->beginGeldigheid),
    
'end' => strval($xml->voorkomen->Voorkomen->eindGeldigheid ?? null) ?: null,
    
'spaces' => xml_array($xml->ligtAan->OpenbareRuimteRef ?? []),
    
'places' => xml_array($xml->ligtIn->WoonplaatsRef ?? [])
  ];
});

print(
"\nTotal " number_format(count($numbers)) . " numbers\n");