source: subversion/applications/utils/where_are_they/where_am_i.py @ 34613

Last change on this file since 34613 was 7204, checked in by nickburch, 11 years ago

Update to support osm v0.5

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 8.1 KB
Line 
1#!/usr/bin/python
2#                                       Where am I?
3#                                       -----------
4#
5# An OpenStreetMap powered python script to answer the question.
6#
7# Supports the following:
8#   What main road am I on?
9#     lat=...,long=....,dist=....,road
10#   What main roads am I near?
11#     lat=...,long=....,dist=....,roads
12#   What place am I in?
13#     lat=...,long=....,dist=....,place
14#   What places am I near?
15#     lat=...,long=....,dist=....,places
16#
17# Can output as html (format=html - default) or xml (format=xml)
18#  or OSM XML (format=osm) or GPX XML (format=gpx)
19#
20# Requires that Plant.OSM has been loaded into a local postgres database,
21#  using planetosm-to-db.pl (currently needs OSM 0.5)
22#
23# See http://wiki.openstreetmap.org/index.php/Where_Are_They
24#
25# GPL
26#
27# Nick Burch
28#               v0.08  (27/03/2008)
29
30import sys
31from osm_io_helper import hasOpt, getOpt, hasAnyOpts, printHTTPHeaders, renderResults
32from geo_helper import calculate_distance_and_bearing
33from mini_osm import mini_osm_pgsql
34
35# What highway and class options we want
36segment_types = [ "highway", "class" ]
37main_roads = [ "motorway", "trunk", "primary", "secondary" ]
38
39# Where are they?
40lat = None
41long = None
42if hasOpt("lat"):
43        lat = getOpt("lat")
44if hasOpt("latitude"):
45        lat = getOpt("latitude")
46if hasOpt("long"):
47        long = getOpt("long")
48if hasOpt("longitude"):
49        long = getOpt("longitude")
50
51# Ensure we turn lat/long of "" into None
52if (not lat == None) and (not len(lat)):
53        lat = None
54if (not long == None) and (not len(long)):
55        long = None
56
57# Do they want HTML, XML, or OSM?
58formats = [ "html", "xml", "osm", "gpx" ]
59format = ""
60if hasOpt("format"):
61        format = getOpt("format")
62if not formats.__contains__(format):
63        format = formats[0]
64
65# Do the HTTP headers
66printHTTPHeaders(format)
67
68# What did they request?
69type = 'help'
70subtype = None
71single = 0
72if hasOpt("type"):
73        type = getOpt("type")
74if hasOpt("road"):
75        type = "road"
76        single = 1
77if hasOpt("roads"):
78        type = "road"
79if hasOpt("place"):
80        type = "node"
81        subtype = ("place",None)
82        single = 1
83if hasOpt("places"):
84        type = "node"
85        subtype = ("place",None)
86if hasOpt("node_type") and not getOpt("node_type") == '':
87        type = "node"
88        if hasOpt("node_value") and not getOpt("node_value") == '':
89                subtype = (getOpt("node_type"), getOpt("node_value"))
90        else:
91                subtype = (getOpt("node_type"), None)
92
93# What distance do they want to search over?
94distance = 250
95if hasOpt("dist"):
96        distance = getOpt("dist")
97if hasOpt("distance"):
98        distance = getOpt("distance")
99distance = int(distance)
100# Max distance - depends on search type
101if type == "node" and not subtype == None:
102        # Can afford to allow it to be quite big (500km)
103        if distance > 500000:
104                distance = 500000
105else:
106        # Limit to 5km, as will fetch all nodes on way or another (5km)
107        if distance > 5000:
108                distance = 5000
109
110
111# Avoid XSS from user string inputs
112if not subtype == None:
113        new_st0 = subtype[0]
114        new_st1 = subtype[1]
115        new_st0 = new_st0.replace('<','&gt;')
116        if not new_st1 == None:
117                new_st1 = new_st1.replace('<','&gt;')
118        subtype = (new_st0,new_st1)
119
120# Work out the name for this type
121type_name = type
122if not subtype == None:
123        if subtype[1] == None:
124                type_name = subtype[0]
125        else:
126                type_name = subtype[1]
127type_name = type_name[0:1].upper() + type_name[1:]
128if not type_name[-1:] == 's':
129        if type_name[-1:] == 'y':
130                type_name = type_name[0:-1] + "ie"
131        type_name = type_name + 's'
132
133# Connect to the database
134miniosm = mini_osm_pgsql()
135
136# ##########################################################################
137
138def displayError(message):
139        global format
140        global type
141        global subtype
142
143        global lat
144        global long
145        global distance
146
147        if format == "xml" or format == "osm" or format == "gpx":
148                print '<?xml version="1.0" encoding="UTF-8"?>'
149                print '<error>%s</error>' % message
150        if format == "html":
151                title = "Error - %s" % message
152                if not hasAnyOpts():
153                        title = "Where Am I?"
154
155                if lat == None:
156                        lat = ""
157                if long == None:
158                        long = ""
159
160                print "<html><head><title>%s</title></head><body>" % title
161                print "<h1>%s</h1>" % title
162                print "<h3>More Information:</h3>"
163                print "<p>For more information, see <a href='http://wiki.openstreetmap.org/index.php/Where_Are_They'>http://wiki.openstreetmap.org/index.php/Where_Are_They</a>.</p>"
164                print "<form method='get'>"
165                print "<h3>Where to search:</h3>"
166                print "<p><label for='lat'>Latitude:</label> <input type='text' name='lat' value='%s' ></p>" % lat
167                print "<p><label for='long'>Longitude:</label> <input type='text' name='long' value='%s' /></p>" % long
168                print "<p><label for='dist'>Distance:</label> <input type='text' name='dist' value='%s' /> meters (max 5000, or 500,000 when doing node type search)</p>" % distance
169                print "<h3>What to search for:</h3>"
170                print "<p><input type='checkbox' name='roads' checked='yes' />Find Roads</p>"
171                print "<p><i>or</i> <input type='checkbox' name='places' />Find Places</p>"
172                print "<p><i>or</i> <label for='node_type'>Node Type:</label> <input type='text' name='node_type' /><br />"
173                print "   <i>and</i> <label for='node_value'>Node Type Value:</label> <input type='text' name='node_value' /></p>"
174                print "<h3>Output Format:</h3>"
175                print "<p><select name='format'>"
176                print "  <option value='html' selected>HTML</option>"
177                print "  <option value='xml'>XML (simple)</option>"
178                print "  <option value='osm'>OSM</option>"
179                print "  <option value='gpx'>GPX (waypoints)</option>"
180                print "</select></p>"
181                print "<h3>Perform search:</h3>"
182                print "<p><input type='submit' value='search' /></p>"
183                print "</form>"
184                print "</body></html>"
185
186        sys.exit()
187
188def displayResults(type,objects):
189        global format
190        global lat
191        global long
192        title = "%s near to %f,%f" % (type,lat,long)
193        renderResults(type,objects,format,title,"distbearing")
194
195# ##########################################################################
196
197def sortByDistance(x,y):
198        if x["distance"] == y["distance"]:
199                return 0
200        if x["distance"] > y["distance"]:
201                return 1
202        return -1
203
204# ##########################################################################
205
206# Check we have lat and long
207if lat == None:
208        displayError("Latitude must be supplied")
209if long == None:
210        displayError("Longitude must be supplied")
211lat = float(lat)
212long = float(long)
213
214# Do the work
215if type == "road":
216        # We want to know about nodes, segments and ways
217        # So, go and fetch them for our area
218        nodes = miniosm.getNodesInArea(lat,long,distance)
219
220        ways = {}
221        if len(nodes):
222                ways = miniosm.getWaysForNodes(nodes)
223
224        # Push interesting tags down as main keys
225        interesting_tags = ["name","ref"] + segment_types
226        miniosm.splatTagsOntoObjects(ways, interesting_tags)
227
228        # Grab just the ways of interest
229        highways = miniosm.filterWaysByTags(ways,nodes,segment_types,main_roads)
230
231        # Calculate the distance from the segment waypoints
232        miniosm.calculateDistanceToWays(lat,long,highways,nodes)
233        highways.sort(sortByDistance)
234
235        # Sort out the type, and only have one with each name+ref
236        seen_ways = {}
237        want_highways = []
238        for way in (highways):
239                tup = (way["name"],way["ref"])
240                if not seen_ways.has_key(tup):
241                        seen_ways[tup] = way
242
243                        # Now do type
244                        type = None
245                        for tag in (segment_types):
246                                if not way[tag] == None:
247                                        type = way[tag]
248                        way["type"] = type
249                        want_highways.append(way)
250
251        # All done, display
252        if len(want_highways) == 0:
253                displayResults("Roads", want_highways)
254        elif single:
255                displayResults("Road", [want_highways[0]])
256        else:
257                displayResults("Roads", want_highways)
258
259elif type =="node":
260        # We only need to get nodes
261        # When we do, filter by tag name, and optionally also tag value
262        nodes = miniosm.getNodesInAreaWithTag(lat,long,distance,subtype[0],subtype[1])
263
264        if len(nodes) == 0:
265                displayResults(type_name, [])
266                sys.exit(0)
267
268        # Push the tags that are interesting down to the nodes
269        miniosm.splatTagsOntoObjects(nodes, ["name",subtype[0]])
270
271        # Sort these nodes by their distance
272        for node in (nodes.values()):
273                dist_bearing = calculate_distance_and_bearing(lat,long,node["lat"],node["long"])
274                node["distance"] = dist_bearing[0]
275                node["bearing"] = dist_bearing[1]
276                # Also splat into type
277                node["type"] = node[subtype[0]]
278        node_array = nodes.values()
279        node_array.sort(sortByDistance)
280
281        # All done, display
282        if len(node_array) == 0:
283                displayResults(type_name, [])
284        elif single:
285                displayResults(type_name, [node_array[0]])
286        else:
287                displayResults(type_name, node_array)
288else:
289        displayError("No search type given")
Note: See TracBrowser for help on using the repository browser.