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:
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“