Compare commits
14 Commits
master
...
webinterfa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
285c849adc | ||
|
|
1f3be8fd37 | ||
|
|
223a2b12b6 | ||
|
|
afb5cc3336 | ||
|
|
dba6ed4b0b | ||
|
|
6eda1c26e2 | ||
|
|
0b7184185f | ||
|
|
2d293f3a5c | ||
|
|
50085391d1 | ||
|
|
2b62bc4725 | ||
|
|
d0dd884a23 | ||
|
|
293e29409a | ||
|
|
8f2ac69835 | ||
|
|
f21551c06e |
@@ -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
index.html
Normal file
29
index.html
Normal 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
osmsearch.py
Normal file
120
osmsearch.py
Normal 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
style.css
Normal file
36
style.css
Normal 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;
|
||||
}
|
||||
98
websearch.py
Executable file
98
websearch.py
Executable 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>
|
||||
""")
|
||||
Reference in New Issue
Block a user