Visualisierung von Adresslisten

Problemstellung

„Ach, was machst du denn hier?!“ hieß es letztens von einem Kita-Papa meines Kindes, als er mich nahe unseres Zuhauses ebenfalls auf dem Weg zur Kita traf. Bis dahin wusste ich allerdings auch nicht, dass sie bei uns ums Eck wohnt.
Daher die Idee: Alle Adressen zu visualisieren, da wir Bilder eben einfacher begreifen. Wichtig für mich dabei:

  • Es sollte dem Datenschutz genügen -> Als Visualisierung nicht gerade GoogleMaps o.ä. nutzen, die Speicherung der Daten idealerweise offline
  • Für spätere Erweiterungen per Script aus einer Liste erstellbar (*.csv, *.xlsx,..)
  • Gruppen differenzierbar durch unterschiedliche Farben und/oder Icons

Lösungsansatz manuell

Adressen in Geopunkte/Koordianten

Am Anfang steht, nach der Bereinigung der Daten geht’s an die Abfrage der Koordinaten. Man könnte nun jede einzelne bei GoogleMaps suchen und sich aus der Adresszeile die Koordinaten heraus nehmen(wäre blöd wegen des Datenschutzes). Das selbe geht auch mit OpenStreetMap über den Perma-Link. Aber es geht auch noch schöner und sauberer:

https://nominatim.openstreetmap.org/search?q=<Strasse>+<Hausnummer>+<PLZ>+<Ort>&format=json&polygon=0&addressdetails=0

Damit bekommt man schon mal ein Datenpaket mit Adresse, Infos, Koordinaten, ggf. Eckpunkten (für größere Grundstücke oder zB. Stadtgrenzen interessant), etc. Als Beispiel sollen hier mal die örtlichen Fast-Food-Restaurants zweier bekannten Unternehmen:

https://nominatim.openstreetmap.org/search?q=Pagenstecherstrasse+50A+49090+Osnabrück&format=json&polygon=0&addressdetails=0

Als Ergebnis wird folgendes im JSON-Format zurück geliefert:

0
  place_id	942461
  licence	"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright"
  osm_type	"node"
  osm_id	306391999
  boundingbox	
    0	"52.2893061"
    1	"52.2894061"
    2	"8.02859"
    3	"8.02869"
  lat	"52.2893561"
  lon	"8.02864"
  display_name	"Burger King, 50A, Pagenstecherstraße, Hafen, Osnabrück, Niedersachsen, 49090, Deutschland"
  class	"amenity"
  type	"fast_food"
  importance	0.33001
  icon	"https://nominatim.openstreetmap.org/ui/mapicons/food_fastfood.p.20.png"

Gebraucht wird gerade nur die lat und lon, also den Längen- und Breitengrad im Dezimalformat und passt auch so direkt vom Format. Für die weitere Nutzung eignet sich die Speicherung in dem Format: <lon>,<lat>,0
Nun sollten die Datensätze in diesem Beispiel folgende Infos beinhalten:

  • Kette
  • Koordinaten im Format <lon>,<lat>,0
  • Beschreibung zum Datenpunkt, zB. die Adresse

Erzeugung des Keyhole Markup Language-Files – kurz kml

Das kml-Format dürfte von den meisten mit Exporten bei Garmin oder Google-Earth in Verbindung gebracht werden. Genaueres gibt’s bei Wiki oder auch in der Dokumentation bei/von Google.
Der Aufbau ist recht simple, wenn man mit XML vertraut ist (hier nur stark vereinfacht für das Szenario dargestellt):

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <Folder>
      <name>Name der Gruppe</name>
      <Style id="eineID">
        <IconStyle id="mystyle">
          <Icon>
            <href>Link zu nem Image)</href>
            <scale>1.0</scale>
          </Icon>
        </IconStyle>
      </Style>
      .... more Styles
      <Placemark>
        <name>Name des PlaceMarks</name>
        <description>Eine Beschreibung (Adresse, Infos, What ever)</description>
        <styleUrl>#eineID</styleUrl>
        <Point>
          <coordinates>lon,lat,0</coordinates>
        </Point>
      </Placemark>
      .... more Placemarks
    </Folder>
    .... more Folder
  </Document>
</kml>

Mit verschiedenen Styles kann man nun die unterschiedlichen Ketten/Unternehmen differenzieren, die einzelnen Restaurants werden als Placemarks nach einander aufgelistet. Wichtig: Die ID zum Style, im Beispiel eineID bekommt im Placemark noch ein # vorgesetzt.
Kleiner Tipp an der Stelle: Es gibt diverse Tools, die Code-Highlighting für XML direkt unterstützen, zB. PSPad, der Standard-Editor unter Ubuntu, oder auch MS Visual Studio Code.

Darstellung der Punkte

Die kml-Datei ist fertig, alle Punkte sind beschrieben und die Gruppen unterschiedlich gekennzeichnet. Nun geht’s an die Wahl der Karte.
Wie oben beschrieben, kommt für mich GoogleMaps nicht in Frage, zumindest nicht bei privateren Listen.

Für all die, die lieber die einfache Alternative nutzen möchten, kommt GoogleEarth in Frage, was immerhin lokal läuft. Was am Ende aber an Google geht bleibt fraglich und möchte ich hier nicht beurteilen.
Eine weiter Option wäre OpenStreetMaps (mit einem kleinen Umweg und allerdings auch online abgelegten Daten), oder ein darauf basierendes quell offenes Tool wie Marble (hier muss jedoch die kml noch etwas weiter angepasst werden). Auf mobilen Endgeräten lassen sich zum Teil auch kml-Files direkt in die OSM-App importieren, was Online so nicht einfach möglich ist.

Mir gefiel an dieser Stelle die Lösung mit der hochgeladenen kml-Datei und der Darstellung in der OpenStreetMaps via Umweg, was eigentlich auch nicht so aufwendig ist:

https://osm.quelltextlich.at/viewer-js.html?kml_url=<URl_zur_.kml>

Und: Es lässt sich nun schön online zeigen. Aber immer Vorsicht: Das Internet vergisst schwer bis nichts. Auch Dienste, die nur darstellen, könnten Informationen speichern.

Denken wir Groß

Bei den paar Adressen lässt sich das noch eben manuell handhaben, bei einigen hunderten nicht mehr. Zudem sollte die Lösung möglichst nicht an größere Programme gebunden sein (bis auf die Visualisierung).

Daher als Ansatz: Man nehme eine *.csv mit einem Format wie

Gruppe;Name;Beschreibung;Adresse
Macs;Mac1;Nikolaiort;Nikolaiort 1-2 49074 Osnabrück-Innenstadt
Macs;Mac2;Theodor-Heuss-Platz;Theodor-Heuss-Platz 1 49074 Osnabrück-Innenstadt
Macs;Mac3;Pagenstecher;Pagenstecherstr. 72 49090 Osnabrück-Hafen
Macs;Mac4;Hannoversche;Hannoversche Str. 45 49084 Osnabrück-Fledder
Macs;Mac5;Hauptstrasse;Hauptstr. 105 49205 Hasbergen-Gaste
BKing;BKay1;Moserstrasse;Moserserstr. 51 49074 Osnabrück
BKing;BKay2;Pagenstecher;Pagenstecherstr. 50 A 49090 Osnabrück
BKing;BKay2;Hannoversche;Hannoversche Str. 74 49084 Osnabrück

und lasse diese dann durch ein bisschen Code in der Shell oder einer Hochsprache laufen. Um mal zu zeigen, was alles direkt mit Bordmitteln in Unix möglich ist, bleibt es hier alles in der Shell. Die *.csv lässt sich mit diversen Tools bearbeiten, notfalls aber eben auch mit jedem 08/15-Editor.

Data preparation

Da nicht immer Sichergestellt ist, dass der Nutzer auch alles sortiert hat (vielleicht wurden Manuell noch Datensätze ergänzt), wird einmal der Header von den Daten getrennt, diese dann sortiert und alles wieder zusammengefügt (hier nach den ersten beiden Spalten):

# Daten sortieren
sed -n -e '1,1w head.tmp' -e '2,$w tail.tmp' daten.csv
sort -t\; -k 1,1n -k 2,2n tail.tmp | cat head.tmp - > daten.csv

Parse the Line

Erstmal müssen die Daten der *.csv-Datei geholt und geparst werden:

#! /bin/bash
# modifiziertes Beispiel 3.1 von https://www.baeldung.com/linux/csv-parsing
while IFS=";" read -r group label descrpt adress
do
    echo "Group: $group"
    echo "Label: $label"
    echo "desc.: $descrpt"
    echo "Adress: $adress"
    echo ""
done < <(tail -n +2 daten.csv)

Get the Information

Mit den Daten lassen sich nun die Koordinaten bei Openstreetmap abfragen und das JSon parsen (erstmal nur einen Einzelschritt):

#!/bin/bash
# zum Test eine Adresse
adress="Hannoversche+Strasse+74+49084+Osnabrück"
# JSon String holen
wget -q "https://nominatim.openstreetmap.org/search?q="$adress"&format=json&polygon=0&addressdetails=0"
mv ./"search?q="$adress"&format=json&polygon=0&addressdetails=0" ./output.json
# JSon parsen und bereinigen
koordinates=$(jq ".[0].lon, .[0].lat, 0" ./output.json | sed "s/ /,/g" | sed "s/\"//g")
echo $koordinates

Write to

Daten holen, verarbeiten und Abfragen klappt, dann muss nur noch alles in eine Datei geschrieben werden:

#!/bin/bash
echo Irdeng ein txt >> test.txt
echo noch eine Zeile >> test.txt

Die Lösung in der Shell

Alle Puzzle-Stücke sind da, nun muss alles nur noch zusammengebracht werden: Erst werden die Daten gelesen, in die Schleife kommt dann gleich auch die Anfrage der Koordinaten und alles zusammen wird in die finale Datei geschrieben.

#! /bin/bash
# Outputfilename.kml
outfile=output.kml
# Hilfsgriff für die Gruppen
groupold=""

# Daten sortieren
sed -n -e '1,1w head.tmp' -e '2,$w tail.tmp' daten.csv
sort -t\; -k 1,1n -k 2,2n tail.tmp | cat head.tmp - > daten.csv

# Header der KML
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $outfile
echo "<kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\">" >> $outfile
echo "    <Document>" >> $outfile
echo "      <name>Lesezeichen</name>" >> $outfile

# nun das Iterieren über die einzelnen Einträge
while IFS=";" read -r group label descrpt adress
do
    # Bei jeder neuen Gruppe (hier mit dem kleinen Kunstgriff)
    if [[ ! $groupold == $group ]]; then
    	# ist das nicht der erste Eintrag : Closetag
    	if [[ ! $groupold == "" ]]; then
    	    echo "      </Folder>" >> $outfile
    	fi
        echo "      <Folder>" >> $outfile
        echo "        <name>$group</name>" >> $outfile
        echo "        <Style id=\"$group\">" >> $outfile
        echo "          <IconStyle id=\"mystyle\">" >> $outfile
        echo "            <Icon>" >> $outfile
        echo "              <href>http://maps.google.com/mapfiles/kml/paddle/red-blank.png</href>" >> $outfile
        echo "              <scale>1.0</scale>" >> $outfile
        echo "            </Icon>" >> $outfile
        echo "          </IconStyle>" >> $outfile
        echo "        </Style>" >> $outfile
        groupold=$group
    fi
    # Bereinigung der Adresse , -> " " und " " in +
    adress=$(echo $adress | sed "s/,/ /g" | sed "s/ /+/g")
    echo $adress
    
    # Beschaffung der Koordinaten
    # JSon String holen
    wget -q "https://nominatim.openstreetmap.org/search?q="$adress"&format=json&polygon=0&addressdetails=0"
    # Umbenennen (auch damit er immer wieder überschrieben wird) 
    mv ./"search?q="$adress"&format=json&polygon=0&addressdetails=0" ./output.json
    # JSon parsen und bereinigen
    lon=$(jq ".[0].lon" ./output.json)
    lat=$(jq ".[0].lat" ./output.json)
    koordinates=$(echo "$lon,$lat,0" | sed "s/\"//g")
    
    # bau des Placemarks
    echo "        <Placemark>" >> $outfile
    echo "          <name>$label</name>" >> $outfile
    echo "          <description>$descrpt</description>" >> $outfile
    echo "          <styleUrl>#$group</styleUrl>" >> $outfile
    echo "          <Point>" >> $outfile
    echo "            <coordinates>$koordinates</coordinates>" >> $outfile
    echo "          </Point>" >> $outfile
    echo "        </Placemark>" >> $outfile
    echo -- $cadress
    echo ""
done < <(tail -n +2 daten.csv)

# Schließe den Ordner und alles andere
echo "      </Folder>" >> $outfile
echo "  </Document>" >> $outfile
echo "</kml>" >> $outfile

Das Script wird dann mit bash./<file.sh> ausgeführt. Am Ende werden allerdings noch alle Placemarks mit den gleichen, roten Pin dargestellt. Das lässt sich über eine weitere csv-Datei als Datenbank erledigen, oder hier auch gut händisch (den Link im <Icon>-Tag anpassen).

Was dann nicht ausbleibt, sind invalide Daten:

Moserserstr.+51+49074+Osnabrück
null null 0

Hier wurde die Möserstrasse als Möserserstrasse angegeben. Das kann bei großen Datenmengen immer mal passieren. Daher könnte man im weiteren noch optimieren:

  • Datenökonomie: Daten die schon bekannt sind, werden nicht nochmal angefragt.
  • Validieren von Eingangsdaten: Gibt es die Adresse so? Und was passiert, wenn dem nicht so ist? Ignorieren, weil nur 1:1000?
  • Bereinigung des Codes: Hier ist nun alles vereinfacht in ein Script gedrückt. Das ist nicht schön aber eben funktional.
  • Farbliche Unterscheidung: zB. durch berechnete Farbwerte

Für mich reicht es an dieser Stelle. Bei deutlich größeren Punktemengen, vielleicht mit einer zeitlichen Komponente, wären farbliche Abgrenzungen vielleicht nochmal interessant. Da es hier jedoch um sehr wenige, unterschiedliche Gruppen geht, ist die eventuelle, manuelle Nacharbeit, völlig im Rahmen. Wie das ganze in der PowerShell gelöst werden kann, gibts in einem anderen Beitrag.

Ein Gedanke zu „Visualisierung von Adresslisten“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert