Last active
April 3, 2019 23:17
-
-
Save grigory-rechistov/ca69c0b8cb051532c6c31f884fc64191 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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