Skip to content

Instantly share code, notes, and snippets.

@grigory-rechistov
Last active April 3, 2019 23:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grigory-rechistov/ca69c0b8cb051532c6c31f884fc64191 to your computer and use it in GitHub Desktop.
Save grigory-rechistov/ca69c0b8cb051532c6c31f884fc64191 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
# Ta in en GeoJSON fil av QGIS,
# producera en ny GeoJSON med nya etiketter och brus tillagt till koordinater
# Källor och referenser:
# - http://www.naturvardsverket.se/Sa-mar-miljon/Kartor/Nationella-Marktackedata-NMD/
# - http://gpt.vic-metria.nu/data/land/NMD/NMD_Produktbeskrivning_NMD2018Basskikt_v1_0.pdf
# - https://lists.openstreetmap.org/pipermail/talk-se/2019-March/003537.html
import sys
import re
import random
def usage():
print("Användning: %s <fil> [brus]" % sys.argv[0], file = sys.stderr)
sys.exit(1)
def get_attributemap():
"""Returnera en dict 'numeriskt värde -> OSM etiketter'
"""
mapper = dict()
# Det finns bättre metoder att spåra vattenytor. Upplösningen på
# byggnader och industrimark är inte hög nog heller, därför ingår bara
# skog och åkermark till urvalet nedan.
## 111 Tallskog utanför våtmark
## 112 Granskog utanför våtmark
## 113 Barrblandskog utanför våtmark
mapper["111"]='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "needleleaved", "genus": "pinus", "leaf_cycle": "evergreen"'
mapper["112"]='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "needleleaved", "genus": "picea", "leaf_cycle": "evergreen"'
mapper["113"]='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "needleleaved", "leaf_cycle": "evergreen"'
## 114 Lövblandad barrskog utanför våtmark
mapper['114']='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "mixed", "leaf_cycle": "mixed"'
## 115 Triviallövskog utanför våtmark
## 116 Ädellövskog utanför våtmark
## 117 Triviallövskog med ädellövinslag utanför våtmark
mapper['115']='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "broadleaved", "leaf_cycle": "deciduous"'
mapper['116']='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "broadleaved", "leaf_cycle": "deciduous"'
mapper['117']='"source": "NV NMD2018", "landuse": "forest", "leaf_type": "broadleaved", "leaf_cycle": "deciduous"'
## 118 Temporärt ej skog utanför våtmark
mapper['118']='"source": "NV NMD2018", "landuse": "forest", "natural": "scrub"'
## 121 Tallskog på våtmark
## 122 Granskog på våtmark
## 123 Barrblandskog på våtmark
mapper['121']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "needleleaved", "genus": "pinus", "leaf_cycle": "evergreen"'
mapper['122']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "needleleaved", "genus": "picea", "leaf_cycle": "evergreen"'
mapper['123']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "needleleaved", "leaf_cycle": "evergreen"'
## 124 Lövblandad barrskog på våtmark
mapper['124']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "mixed", "leaf_cycle": "mixed"'
## 125 Triviallövskog på våtmark
## 126 Ädellövskog på våtmark
## 127 Triviallövskog med ädellövinslag på våtmark
mapper['125']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "broadleaved", "leaf_cycle": "deciduous"'
mapper['126']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "broadleaved", "leaf_cycle": "deciduous"'
mapper['127']='"source": "NV NMD2018", "natural": "wetland", "landuse": "forest", "leaf_type": "broadleaved", "leaf_cycle": "deciduous"'
## 128 Temporärt ej skog på våtmark
#mapper['128']='"source": "NV NMD2018", "landuse": "forest", "natural": wetland'
## 2 Våtmark
#mapper['2']='"source": "NV NMD2018", "natural": wetland'
## 3 Åkermark
mapper['3']='"source": "NV NMD2018", "landuse": "farmland"'
## 41 Övrig öppen mark utan vegetation
## 42 Övrig öppen mark med vegetation
mapper['42']='"source": "NV NMD2018", "landuse": "meadow"'
## 51 Exploaterad mark, byggnad
#mapper['51']='"source": "NV NMD2018", "building": "yes", "note": "Needs surveying"'
## 52 Exploaterad mark, ej byggnad eller väg/järnväg
#mapper['52']='"source": "NV NMD2018", "landuse": "industrial", "note": "Needs surveying"'
## 53 Exploaterad mark, väg/järnväg
## 61 Sjö och vattendrag
#mapper['61']='"source": "NV NMD2018", "natural": "water"'
## 62 Hav
#mapper['62']='"source": "NV NMD2018", "natural": "water"'
return mapper
def replace_dn_with_tags(line, mapper):
'''Ersätter { "DN": nummer } i line med lämpliga OSM-etiketter från mapper'''
# "properties": { "cat": 3, "DN": 41 }
m = re.match(r'(.*"properties":\s*\{).*"DN"\s*:\s*(\d+)\s*(\}.*)', line)
assert not m is None
category = m.group(2)
if not category in mapper:
return None # ej intresserad i etiketten
tags = mapper[category]
return m.group(1) + tags + m.group(3)
def count_points(line):
count = 0
tokens = line.split()
newline = ""
seen_coordinates = False
for token in tokens:
if token.find("coordinates") != -1:
seen_coordinates = True
continue
if seen_coordinates:
# exempel: "18.149303477187672,"
m = re.match(r"([^\d]*)(\d+)(\.?)(\d*)(.*)", token)
if m:
count += 1
return int(count / 2) # två nummer i koordinatparet
def add_noise(line, brus):
"""Lägg till brus till alla numeriska koordinater i line"""
tokens = line.split()
newline = ""
seen_coordinates = False
for token in tokens:
if token.find("coordinates") != -1:
# print (">>> coordi", file = sys.stderr)
seen_coordinates = True
newline += token + ' '
continue
if seen_coordinates:
# exempel: "18.149303477187672,"
# print (">>> orig", token, file = sys.stderr)
m = re.match(r"([^\d]*)(\d+)(\.?)(\d*)(.*)", token)
if m:
str_num = m.group(2)+ m.group(3) + m.group(4)
# print (">>> match", str_num, file = sys.stderr)
num = float(str_num)
num += brus * (random.random() - 0.5)
token = m.group(1) + str(num) + m.group(5)
# print (">>> newtoken", token, file = sys.stderr)
newline += token + ' '
return newline
def remove_first_node(line):
"""Första och sista noder i line har samma koordinater som förvirrar OSM.
Ta den första noden bort."""
#"coordinates": [ [ [ 16.899063660620882, 58.50760921186032 ],
new_line = re.sub(r'("coordinates":\s*\[\s*\[\s*)(\[\s*\d+\.?\d*\s*,\s*\d+\.?\d*\s*\]\s*,)',
r'\1',
line)
assert new_line != line # något har hänt
return new_line
def main(argv):
random.seed()
# exempel på rad:
# { "type": "Feature", "properties": { "DN": 116 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 18.149303477187672, 59.449025507144079 ], ...
if len(argv) < 2 or len(argv) > 3:
usage()
filnamn = argv[1]
if len(argv) == 3:
# 10 meter är cirka 9e-05
brus = float(argv[2])
else:
brus = 0.0
point_limit = 13 # TODO acceptera som skriptens argument
mapper = get_attributemap()
# TODO ha en riktig JSON-formatläsare istället för den nedanstående
# radbearbetningen, vilken ska bli mer pålitlig
with open(filnamn) as f:
for line in f.readlines():
if line.find('"DN"') == -1: # ej hittat
print(line, end = '') # filens huvud och svans oförändrade
continue
point_count = count_points(line)
if point_count < point_limit:
# print (">>> liten polygon: %d punkter" % point_count, file = sys.stderr)
continue # skippa mindre objekt som bara skapar visuellt brus
line = replace_dn_with_tags(line, mapper)
if line is None:
continue
# print (">>> före: %d punkter" % count_points(line), file = sys.stderr)
line = remove_first_node(line)
# print (">>> efter: %d punkter" % count_points(line), file = sys.stderr)
if brus != 0.0:
line = add_noise(line, brus)
print(line)
# TODO: slutlig komma kan saknas som gör att JSON:en blir ogiltig.
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment