Compare commits

...
14 Commits
5 changed files with 283 additions and 87 deletions

No files matched your search

-87
View File
@@ -1,87 +0,0 @@
"""
Script to retrieve all Openstreetmap items matching a query and save
them as GPX Waypoints to a given output file in GPX format.
"""
__author__ = "fordprefect"
__date__ = "2020-03-2
__version__ = "0.1"
import gpxpy
import requests
import os
import sys
assert sys.version_info >= (3, 6), "At least Python 3.8 required due to fStrings. Replace all f\"…\" and comment this line to enable running in lower versions (but really, you should just update your Python version…)."
#####################################
######## configuration
#####################################
# search url of nominatim (don't change if you don't know what you are doing!)
url = "https://nominatim.openstreetmap.org/search/"
# search arguments, change query here
# for all supported keys see http://nominatim.org/release-docs/latest/api/Search/
args = {"q": "Wasserwanderplatz", "country": "Germany", "format": "json", "limit": 50}
# bounding box defined by two points [x1, y1, x2, y2]
use_boundingbox = True
boundingbox = [53.898, 11.338, 52.700, 14.332]
# output file name
gpxfilename = f"nominatimsearch_{args['q']}.gpx"
# OSM IDs to be ignored (used to find all matching items)
ignore_ids = []
# maximum number of search queries
maxsearchnums = 10
# verbosity
verbosity = 0
#####################################
######## end of configuration
#####################################
## initialize gpx file
if os.path.isfile(gpxfilename):
# open file and parse
if verbosity > 0: print(f"found file, extending")
gpxfile = gpxpy.parse(open(gpxfilename, "r"))
else:
if verbosity > 0: print("creating new file")
gpxfile = gpxpy.gpx.GPX()
# search loop
for i in range(maxsearchnums):
# fetch request
if use_boundingbox:
args["viewbox"] = ",".join(list(map(str, boundingbox)))
if ignore_ids != []:
args["exclude_place_ids"] = ",".join(list(map(str, ignore_ids)))
r = requests.get(url + "?" + "&".join([f"{key}={args[key]}" for key in args]))
if r.status_code != 200:
if verbosity > 0: print(f"Query gone wrong: HTTP returned {r.status_code}")
exit(1)
# parse reply
reply = r.json()
assert isinstance(reply, list), "Unexpected type of reply: " + type(reply)
if len(reply) == 0:
if verbosity > 0: print(f"found {len(ignore_ids)} results in {i-1} iterations, finishing")
break
for point in reply:
pid = point["place_id"]
ignore_ids.append(pid)
lat = point["lat"]
lon = point["lon"]
name = point["display_name"]
gpxfile.waypoints.append(gpxpy.gpx.GPXWaypoint(latitude=lat, longitude=lon, name=name))
# save to disk
with open(gpxfilename, "w") as f:
f.write(gpxfile.to_xml())
if verbosity > 0: print(f"GPX file written to {gpxfilename}")
+29
View File
@@ -0,0 +1,29 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="style.css">
<meta name="robots" content="noindex,nofollow" />
<title>OSM search to GPX</title>
</head>
<body class=body>
<div class=head>
<h3>Export OSM search to GPX file</h3>
</div>
<div class=box>
This form allows you to perform a search of the Open Street Map database (via Nominatim) and exports all results as Waypoints in a GPX file.
<form action=websearch.py method=post>
<input name=query placeholder=Camping></input> Your search query<br>
<input name=boundingbox></input> Give coordinates of bounding box for search in the format of "x1,y1,x2,y2" (optional)<br>
<input name="ignore IDs"></input> Ignore OSM database objects with given IDs (optional)<br>
<input name="Maximum search nubmer" default=10 ></input> Maximum number of runs - increase if results are missing (optional)<br>
<input type=submit name="Submit"><br>
</form>
</div>
<div class=foot>
OSMsearchToGPX - ein Service von <a href=https://dukun.de>dukun.de</a>
</div>
</body>
</html>
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/python
"""
Script to retrieve all Openstreetmap items matching a query and save
them as GPX Waypoints to a given output file in GPX format.
"""
__author__ = "fordprefect"
__date__ = "2020-03-26"
__version__ = "0.1"
import gpxpy
import requests
import os
import sys
assert sys.version_info >= (3, 6), "At least Python 3.8 required due to fStrings. Replace all f\"…\" and comment this line to enable running in lower versions (but really, you should just update your Python version…)."
class OSMSearch():
def __init__(self, args):
"""
TODO: write docstring
"""
self.url = args.pop("url")
self.maxsearchnums = args.pop("maxsearchnums")
self.save_to_file = True if args["output"] == "disk" else False
if self.save_to_file:
self.gpxfilename = args.pop("outputfilename")
self.print_xml = True if args["output"] == "print" else False
self.use_boundingbox = args.pop("use_boundingbox")
self.ignore_ids = args.pop("ignore_ids")
if self.use_boundingbox:
assert isinstance(list, args["boundingbox"])
assert len(args["boundingbox"]) == 4
else:
args.pop("boundingbox")
self.searchargs = args
self.initialize_gpx_file()
self.search()
if self.save_to_file:
self.save_to_disk()
if self.print_xml:
self.print_to_stdout()
def initialize_gpx_file(self):
if not self.save_to_file:
self.gpxfile = gpxpy.gpx.GPX()
return
if os.path.isfile(self.gpxfilename):
raise NotImplementedError("We don't know how to deal with existing files yet, sry")
# TODO: file name collision? either:
# 1) amend file
#self.gpxfile = gpxpy.parse(open(gpxfilename, "r"))
# 2) refuse to work
# 3) alter file name
else:
self.gpxfile = gpxpy.gpx.GPX()
def get_argstring(self):
argstrings = []
if self.use_boundingbox:
argstring.append(",".join(list(map(str, self.searchargs["boundingbox"]))))
if self.ignore_ids != []:
argstrings.append(f"ignore_ids=" + ",".join(list(map(str, self.ignore_ids))))
for arg in self.searchargs:
argstrings.append(f"{arg}={self.searchargs[arg]}")
return "?"+"&".join(argstrings)
def search(self):
"""
"""
for i in range(self.maxsearchnums):
# fetch request
r = requests.get(self.url + self.get_argstring())
if r.status_code != 200:
exit(1)
# parse reply
reply = r.json()
assert isinstance(reply, list), "Unexpected type of reply: " + type(reply)
if len(reply) == 0:
break
for point in reply:
self.ignore_ids.append(point["place_id"])
# TODO use better name than "display_name", maybe add more info to Waypoint
self.gpxfile.waypoints.append(gpxpy.gpx.GPXWaypoint(latitude=point["lat"], longitude=point["lon"], name=point["display_name"]))
def save_to_disk(self):
with open(self.gpxfilename, "w") as f:
f.write(self.gpxfile.to_xml())
def print_to_stdout(self):
print(self.gpxfile.to_xml())
default_args = {
"q": "Camping",
"format": "json",
"limit": 50,
"use_boundingbox": False,
"boundingbox": [],
"outputfilename": "test.gpx",
"ignore_ids": [],
"maxsearchnums": 10,
"url": "https://nominatim.openstreetmap.org/search/",
"output": "print",
}
if __name__ == "__main__":
# cli invocation goes here
# TODO: take user arguments and bring together with sensible defaults above
OSMSearch(default_args)
+36
View File
@@ -0,0 +1,36 @@
div.box {
color:#cecece;
background-image: url('/linen_login.jpg');
background-size: 100%;
width: 80%;
height: 80%;
margin: 0px auto;
overflow: auto;
padding:10px;
border-radius:10px;
box-shadow: 0px 0px 20px #000;
}
body {
background-image:url('/linen.jpg');
background-repeat:repeat;
}
div.head {
width: 80%;
margin: auto;
}
div.foot {
width: 80%;
margin:auto;
margin-top:20px;
}
div.foot a {
color: #0000aa;
}
a {
color:#8080ee;
}
Executable
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/python
"""
CGI interface to osmsearch.py
"""
__author__ = "fordprefect"
__date__ = "2020-03-26"
__version__ = "0.1"
print("Content-type: text/html\n") # makes sure the http connection does not die - do nothing before this!
import cgi
import pathvalidate
import osmsearch
import datetime
import random
import os
import sys
assert sys.version_info >= (3, 6), "At least Python 3.8 required due to fStrings. Replace all f\"…\" and comment this line to enable running in lower versions (but really, you should just update your Python version…)."
import base64
#####################################
######## configuration
#####################################
# default set of arguments
args = {
"q": "Camping",
"format": "json",
"limit": 50,
"use_boundingbox": False,
"boundingbox": [],
"ignore_ids": [],
"maxsearchnums": 10,
"url": "https://nominatim.openstreetmap.org/search/",
"output": "return",
}
immutable_args = ["format", "url", "ouput"]
outputfolder = "downloads"
DEBUG = False
#####################################
######## end of configuration
#####################################
# get cgi args sanitize, and update default args
rawargs = cgi.FieldStorage()
cgi_args = {i: rawargs.getvalue(i) for i in rawargs}
del(rawargs)
parsed_args = {}
try:
cgi_args["q"] = pathvalidate.sanitize_filename(cgi_args["query"])
except KeyError:
if DEBUG:
# local testing
cgi_args["q"] = "Camping"
else:
# we don't need to exit gracefully, whoever is arriving here has fiddled with the request manually
exit()
for arg in cgi_args:
if arg not in immutable_args and arg in args:
parsed_args[arg] = cgi_args[arg]
if DEBUG: print("Parsed argument dict: ", parsed_args)
args.update(cgi_args)
searchresult = osmsearch.OSMSearch(args)
gpxcontent = searchresult.gpxfile.to_xml()
gpxcontent_base64 = base64.b64encode(gpxcontent.encode("utf-8")).decode("utf-8")
print("""
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="style.css">
<meta name="robots" content="noindex,nofollow" />
<title>OSM search to GPX</title>
</head>
<body class=body>
<div class=head>
<h3>Export OSM search to GPX file</h3>
</div>
<div class=box>
<h4>Success</h4>
""" + f"""
<a href="data:text/xml;base64,{gpxcontent_base64}"
""" + """
download="search.gpx">Download GPX file here</a>
<br><br>
<a href=index.html>Return to search page</a>
</div>
<div class=foot>
OSMsearchToGPX - ein Service von <a href=https://dukun.de>dukun.de</a>
</div>
</body>
</html>
""")