pandana 0.2.0__zip → 0.4.0__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {pandana-0.2.0 → pandana-0.4.0}/MANIFEST.in +0 -2
  2. {pandana-0.2.0/pandana.egg-info → pandana-0.4.0}/PKG-INFO +20 -15
  3. {pandana-0.2.0 → pandana-0.4.0}/README.rst +17 -13
  4. {pandana-0.2.0 → pandana-0.4.0}/ez_setup.py +22 -2
  5. pandana-0.4.0/pandana/__init__.py +3 -0
  6. pandana-0.4.0/pandana/loaders/osm.py +189 -0
  7. {pandana-0.2.0 → pandana-0.4.0}/pandana/network.py +177 -170
  8. pandana-0.4.0/pandana/utils.py +27 -0
  9. {pandana-0.2.0 → pandana-0.4.0/pandana.egg-info}/PKG-INFO +20 -15
  10. {pandana-0.2.0 → pandana-0.4.0}/pandana.egg-info/SOURCES.txt +2 -31
  11. pandana-0.4.0/pandana.egg-info/requires.txt +8 -0
  12. {pandana-0.2.0 → pandana-0.4.0}/setup.cfg +0 -1
  13. {pandana-0.2.0 → pandana-0.4.0}/setup.py +42 -34
  14. pandana-0.4.0/src/accessibility.cpp +374 -0
  15. pandana-0.4.0/src/accessibility.h +133 -0
  16. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/DataStructures/Percent.h +5 -5
  17. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/POIIndex/POIIndex.h +2 -1
  18. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/libch.cpp +61 -48
  19. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/libch.h +23 -13
  20. pandana-0.4.0/src/cyaccess.cpp +10356 -0
  21. pandana-0.4.0/src/cyaccess.pyx +176 -0
  22. pandana-0.4.0/src/graphalg.cpp +111 -0
  23. pandana-0.4.0/src/graphalg.h +50 -0
  24. {pandana-0.2.0 → pandana-0.4.0}/src/shared.h +1 -2
  25. pandana-0.2.0/pandana/__init__.py +0 -3
  26. pandana-0.2.0/pandana/loaders/osm.py +0 -394
  27. pandana-0.2.0/pandana/utils.py +0 -38
  28. pandana-0.2.0/pandana.egg-info/pbr.json +0 -1
  29. pandana-0.2.0/pandana.egg-info/requires.txt +0 -5
  30. pandana-0.2.0/src/accessibility.cpp +0 -369
  31. pandana-0.2.0/src/accessibility.h +0 -122
  32. pandana-0.2.0/src/ann_1.1.2/ann2fig/ann2fig.cpp +0 -588
  33. pandana-0.2.0/src/ann_1.1.2/include/ANN/ANN.h +0 -830
  34. pandana-0.2.0/src/ann_1.1.2/include/ANN/ANNperf.h +0 -223
  35. pandana-0.2.0/src/ann_1.1.2/include/ANN/ANNx.h +0 -169
  36. pandana-0.2.0/src/ann_1.1.2/src/ANN.cpp +0 -201
  37. pandana-0.2.0/src/ann_1.1.2/src/bd_fix_rad_search.cpp +0 -61
  38. pandana-0.2.0/src/ann_1.1.2/src/bd_pr_search.cpp +0 -62
  39. pandana-0.2.0/src/ann_1.1.2/src/bd_search.cpp +0 -61
  40. pandana-0.2.0/src/ann_1.1.2/src/bd_tree.cpp +0 -417
  41. pandana-0.2.0/src/ann_1.1.2/src/bd_tree.h +0 -100
  42. pandana-0.2.0/src/ann_1.1.2/src/brute.cpp +0 -109
  43. pandana-0.2.0/src/ann_1.1.2/src/kd_dump.cpp +0 -444
  44. pandana-0.2.0/src/ann_1.1.2/src/kd_fix_rad_search.cpp +0 -183
  45. pandana-0.2.0/src/ann_1.1.2/src/kd_fix_rad_search.h +0 -44
  46. pandana-0.2.0/src/ann_1.1.2/src/kd_pr_search.cpp +0 -219
  47. pandana-0.2.0/src/ann_1.1.2/src/kd_pr_search.h +0 -49
  48. pandana-0.2.0/src/ann_1.1.2/src/kd_search.cpp +0 -210
  49. pandana-0.2.0/src/ann_1.1.2/src/kd_search.h +0 -48
  50. pandana-0.2.0/src/ann_1.1.2/src/kd_split.cpp +0 -428
  51. pandana-0.2.0/src/ann_1.1.2/src/kd_split.h +0 -85
  52. pandana-0.2.0/src/ann_1.1.2/src/kd_tree.cpp +0 -405
  53. pandana-0.2.0/src/ann_1.1.2/src/kd_tree.h +0 -197
  54. pandana-0.2.0/src/ann_1.1.2/src/kd_util.cpp +0 -439
  55. pandana-0.2.0/src/ann_1.1.2/src/kd_util.h +0 -124
  56. pandana-0.2.0/src/ann_1.1.2/src/perf.cpp +0 -136
  57. pandana-0.2.0/src/ann_1.1.2/src/pr_queue.h +0 -125
  58. pandana-0.2.0/src/ann_1.1.2/src/pr_queue_k.h +0 -118
  59. pandana-0.2.0/src/graphalg.cpp +0 -144
  60. pandana-0.2.0/src/graphalg.h +0 -53
  61. pandana-0.2.0/src/nearestneighbor.cpp +0 -106
  62. pandana-0.2.0/src/nearestneighbor.h +0 -37
  63. pandana-0.2.0/src/pyaccesswrap.cpp +0 -514
  64. {pandana-0.2.0 → pandana-0.4.0}/pandana/loaders/__init__.py +0 -0
  65. {pandana-0.2.0 → pandana-0.4.0}/pandana/loaders/pandash5.py +0 -0
  66. {pandana-0.2.0 → pandana-0.4.0}/pandana/testing.py +0 -0
  67. {pandana-0.2.0 → pandana-0.4.0}/pandana.egg-info/dependency_links.txt +0 -0
  68. {pandana-0.2.0 → pandana-0.4.0}/pandana.egg-info/top_level.txt +0 -0
  69. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/BasicDefinitions.h +0 -0
  70. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/Contractor/ContractionCleanup.h +0 -0
  71. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/Contractor/Contractor.h +0 -0
  72. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/DataStructures/BinaryHeap.h +0 -0
  73. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/DataStructures/DynamicGraph.h +0 -0
  74. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/DataStructures/SimpleCHQuery.h +0 -0
  75. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/DataStructures/StaticGraph.h +0 -0
  76. {pandana-0.2.0 → pandana-0.4.0}/src/contraction_hierarchies/src/Util/HyperThreading.h +0 -0
@@ -1,5 +1,3 @@
1
1
  include ez_setup.py
2
2
  include README.rst
3
3
  recursive-include src *.h *.cpp
4
- prune src/ann_1.1.2/sample
5
- prune src/ann_1.1.2/test
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pandana
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Pandas Network Analysis - dataframes of network queries, quickly
5
5
  Home-page: https://udst.github.io/pandana/
6
- Author: Autodesk
6
+ Author: UrbanSim Inc.
7
7
  Author-email: UNKNOWN
8
8
  License: AGPL
9
9
  Description: Pandana
@@ -17,8 +17,6 @@ Description: Pandana
17
17
  :alt: Coverage Status
18
18
  :target: https://coveralls.io/r/UDST/pandana
19
19
 
20
- A nice slideshow showing example code is available
21
- `here <http://bit.ly/1tivyjw>`__.
22
20
 
23
21
  In this case, a picture is worth a thousand words. The image below shows
24
22
  the distance to the *2nd* nearest restaurant (rendered by matplotlib)
@@ -48,8 +46,8 @@ Description: Pandana
48
46
  their environment.
49
47
 
50
48
  We look forward to creative uses of a general library like this - please
51
- let us know when you think you have a great use case with the hashtag
52
- ``#udst``.
49
+ let us know if you think you have a great use case by tweeting us at
50
+ ``@urbansim`` or post on the UrbanSim `forum`_.
53
51
 
54
52
  Docs
55
53
  ----
@@ -64,22 +62,29 @@ Description: Pandana
64
62
  Acknowledgments
65
63
  ---------------
66
64
 
67
- None of this would be possible without the help of Dennis Luxen (now at
68
- MapBox) and his OSRM (https://github.com/DennisOSRM/Project-OSRM). Thank
69
- you Dennis!
70
-
71
- Nearest neighbor queries are performed with the fastest k-d tree around,
72
- i.e. ANN (http://www.cs.umd.edu/~mount/ANN/).
65
+ None of this would be possible without the help of Dennis Luxen and
66
+ his OSRM (https://github.com/DennisOSRM/Project-OSRM). Thank you Dennis!
73
67
 
74
68
  Academic Literature
75
69
  -------------------
76
70
 
77
- I'm currently working on getting a `complete description of the
78
- methodology <https://github.com/fscottfoti/dissertation/blob/master/networks/Foti%20and%20Waddell%20-%20Accessibility%20Framework.pdf?raw=true>`__
79
- published in an academic journal. Please cite this paper when referring
71
+ A `complete description of the
72
+ methodology <http://onlinepubs.trb.org/onlinepubs/conferences/2012/4thITM/Papers-A/0117-000062.pdf>`__
73
+ was presented at the Transportation Research Board Annual Conference in 2012. Please cite this paper when referring
80
74
  to the methodology implemented by this library.
81
75
 
76
+ Related UDST libraries
77
+ ----------------------
78
+
79
+ - `OSMnet`_
80
+ - `UrbanAccess`_
81
+
82
+ .. _forum: http://discussion.urbansim.com/
83
+ .. _OSMnet: https://github.com/udst/osmnet
84
+ .. _UrbanAccess: https://github.com/UDST/urbanaccess
85
+
82
86
  Platform: UNKNOWN
83
87
  Classifier: Development Status :: 3 - Alpha
84
88
  Classifier: Programming Language :: Python :: 2.7
89
+ Classifier: Programming Language :: Python :: 3.5
85
90
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3
@@ -9,8 +9,6 @@ Pandana
9
9
  :alt: Coverage Status
10
10
  :target: https://coveralls.io/r/UDST/pandana
11
11
 
12
- A nice slideshow showing example code is available
13
- `here <http://bit.ly/1tivyjw>`__.
14
12
 
15
13
  In this case, a picture is worth a thousand words. The image below shows
16
14
  the distance to the *2nd* nearest restaurant (rendered by matplotlib)
@@ -40,8 +38,8 @@ queries are a more accurate representation of how people interact with
40
38
  their environment.
41
39
 
42
40
  We look forward to creative uses of a general library like this - please
43
- let us know when you think you have a great use case with the hashtag
44
- ``#udst``.
41
+ let us know if you think you have a great use case by tweeting us at
42
+ ``@urbansim`` or post on the UrbanSim `forum`_.
45
43
 
46
44
  Docs
47
45
  ----
@@ -56,17 +54,23 @@ Pandana is also available.
56
54
  Acknowledgments
57
55
  ---------------
58
56
 
59
- None of this would be possible without the help of Dennis Luxen (now at
60
- MapBox) and his OSRM (https://github.com/DennisOSRM/Project-OSRM). Thank
61
- you Dennis!
62
-
63
- Nearest neighbor queries are performed with the fastest k-d tree around,
64
- i.e. ANN (http://www.cs.umd.edu/~mount/ANN/).
57
+ None of this would be possible without the help of Dennis Luxen and
58
+ his OSRM (https://github.com/DennisOSRM/Project-OSRM). Thank you Dennis!
65
59
 
66
60
  Academic Literature
67
61
  -------------------
68
62
 
69
- I'm currently working on getting a `complete description of the
70
- methodology <https://github.com/fscottfoti/dissertation/blob/master/networks/Foti%20and%20Waddell%20-%20Accessibility%20Framework.pdf?raw=true>`__
71
- published in an academic journal. Please cite this paper when referring
63
+ A `complete description of the
64
+ methodology <http://onlinepubs.trb.org/onlinepubs/conferences/2012/4thITM/Papers-A/0117-000062.pdf>`__
65
+ was presented at the Transportation Research Board Annual Conference in 2012. Please cite this paper when referring
72
66
  to the methodology implemented by this library.
67
+
68
+ Related UDST libraries
69
+ ----------------------
70
+
71
+ - `OSMnet`_
72
+ - `UrbanAccess`_
73
+
74
+ .. _forum: http://discussion.urbansim.com/
75
+ .. _OSMnet: https://github.com/udst/osmnet
76
+ .. _UrbanAccess: https://github.com/UDST/urbanaccess
@@ -39,6 +39,7 @@ except ImportError:
39
39
  DEFAULT_VERSION = "5.7"
40
40
  DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
41
41
 
42
+
42
43
  def _python_cmd(*args):
43
44
  """
44
45
  Return True if the command succeeded.
@@ -130,7 +131,7 @@ def _do_download(version, download_base, to_dir, download_delay):
130
131
 
131
132
 
132
133
  def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
133
- to_dir=os.curdir, download_delay=15):
134
+ to_dir=os.curdir, download_delay=15):
134
135
  to_dir = os.path.abspath(to_dir)
135
136
  rep_modules = 'pkg_resources', 'setuptools'
136
137
  imported = set(sys.modules).intersection(rep_modules)
@@ -160,6 +161,7 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
160
161
  del pkg_resources, sys.modules['pkg_resources']
161
162
  return _do_download(version, download_base, to_dir, download_delay)
162
163
 
164
+
163
165
  def _clean_check(cmd, target):
164
166
  """
165
167
  Run the command to download target. If the command fails, clean up before
@@ -172,6 +174,7 @@ def _clean_check(cmd, target):
172
174
  os.unlink(target)
173
175
  raise
174
176
 
177
+
175
178
  def download_file_powershell(url, target):
176
179
  """
177
180
  Download the file at url to target using Powershell (which will validate
@@ -191,6 +194,7 @@ def download_file_powershell(url, target):
191
194
  ]
192
195
  _clean_check(cmd, target)
193
196
 
197
+
194
198
  def has_powershell():
195
199
  if platform.system() != 'Windows':
196
200
  return False
@@ -202,12 +206,15 @@ def has_powershell():
202
206
  return False
203
207
  return True
204
208
 
209
+
205
210
  download_file_powershell.viable = has_powershell
206
211
 
212
+
207
213
  def download_file_curl(url, target):
208
214
  cmd = ['curl', url, '--silent', '--output', target]
209
215
  _clean_check(cmd, target)
210
216
 
217
+
211
218
  def has_curl():
212
219
  cmd = ['curl', '--version']
213
220
  with open(os.path.devnull, 'wb') as devnull:
@@ -217,12 +224,15 @@ def has_curl():
217
224
  return False
218
225
  return True
219
226
 
227
+
220
228
  download_file_curl.viable = has_curl
221
229
 
230
+
222
231
  def download_file_wget(url, target):
223
232
  cmd = ['wget', url, '--quiet', '--output-document', target]
224
233
  _clean_check(cmd, target)
225
234
 
235
+
226
236
  def has_wget():
227
237
  cmd = ['wget', '--version']
228
238
  with open(os.path.devnull, 'wb') as devnull:
@@ -232,8 +242,10 @@ def has_wget():
232
242
  return False
233
243
  return True
234
244
 
245
+
235
246
  download_file_wget.viable = has_wget
236
247
 
248
+
237
249
  def download_file_insecure(url, target):
238
250
  """
239
251
  Use Python to download the file, even though it cannot authenticate the
@@ -250,8 +262,10 @@ def download_file_insecure(url, target):
250
262
  with open(target, "wb") as dst:
251
263
  dst.write(data)
252
264
 
265
+
253
266
  download_file_insecure.viable = lambda: True
254
267
 
268
+
255
269
  def get_best_downloader():
256
270
  downloaders = (
257
271
  download_file_powershell,
@@ -262,8 +276,10 @@ def get_best_downloader():
262
276
  viable_downloaders = (dl for dl in downloaders if dl.viable())
263
277
  return next(viable_downloaders, None)
264
278
 
279
+
265
280
  def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
266
- to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
281
+ to_dir=os.curdir, delay=15,
282
+ downloader_factory=get_best_downloader):
267
283
  """
268
284
  Download setuptools from a specified location and return its filename
269
285
 
@@ -287,12 +303,14 @@ def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
287
303
  downloader(url, saveto)
288
304
  return os.path.realpath(saveto)
289
305
 
306
+
290
307
  def _build_install_args(options):
291
308
  """
292
309
  Build the arguments to 'python setup.py install' on the setuptools package
293
310
  """
294
311
  return ['--user'] if options.user_install else []
295
312
 
313
+
296
314
  def _parse_args():
297
315
  """
298
316
  Parse the command line for options
@@ -318,6 +336,7 @@ def _parse_args():
318
336
  # positional arguments are ignored
319
337
  return options
320
338
 
339
+
321
340
  def main():
322
341
  """Install or upgrade setuptools and EasyInstall"""
323
342
  options = _parse_args()
@@ -328,5 +347,6 @@ def main():
328
347
  )
329
348
  return _install(archive, _build_install_args(options))
330
349
 
350
+
331
351
  if __name__ == '__main__':
332
352
  sys.exit(main())
@@ -0,0 +1,3 @@
1
+ from .network import Network
2
+
3
+ version = __version__ = '0.4.0'
@@ -0,0 +1,189 @@
1
+ """
2
+ Tools for creating Pandana networks from OpenStreetMap.
3
+
4
+ """
5
+
6
+ import pandas as pd
7
+ import requests
8
+ from osmnet.load import network_from_bbox
9
+
10
+ from .. import Network
11
+
12
+
13
+ def pdna_network_from_bbox(
14
+ lat_min=None, lng_min=None, lat_max=None, lng_max=None, bbox=None,
15
+ network_type='walk', two_way=True,
16
+ timeout=180, memory=None, max_query_area_size=50 * 1000 * 50 * 1000):
17
+ """
18
+ Make a Pandana network from a bounding lat/lon box
19
+ request to the Overpass API. Distance will be in the default units meters.
20
+
21
+ Parameters
22
+ ----------
23
+ lat_min, lng_min, lat_max, lng_max : float
24
+ bbox : tuple
25
+ Bounding box formatted as a 4 element tuple:
26
+ (lng_max, lat_min, lng_min, lat_max)
27
+ network_type : {'walk', 'drive'}, optional
28
+ Specify whether the network will be used for walking or driving.
29
+ A value of 'walk' attempts to exclude things like freeways,
30
+ while a value of 'drive' attempts to exclude things like
31
+ bike and walking paths.
32
+ two_way : bool, optional
33
+ Whether the routes are two-way. If True, node pairs will only
34
+ occur once.
35
+ timeout : int, optional
36
+ the timeout interval for requests and to pass to Overpass API
37
+ memory : int, optional
38
+ server memory allocation size for the query, in bytes.
39
+ If none, server will use its default allocation size
40
+ max_query_area_size : float, optional
41
+ max area for any part of the geometry, in the units the geometry is in
42
+
43
+ Returns
44
+ -------
45
+ network : pandana.Network
46
+
47
+ """
48
+
49
+ nodes, edges = network_from_bbox(lat_min=lat_min, lng_min=lng_min,
50
+ lat_max=lat_max, lng_max=lng_max,
51
+ bbox=bbox, network_type=network_type,
52
+ two_way=two_way, timeout=timeout,
53
+ memory=memory,
54
+ max_query_area_size=max_query_area_size)
55
+
56
+ return Network(
57
+ nodes['x'], nodes['y'],
58
+ edges['from'], edges['to'], edges[['distance']])
59
+
60
+
61
+ def process_node(e):
62
+ """
63
+ Process a node element entry into a dict suitable for going into
64
+ a Pandas DataFrame.
65
+
66
+ Parameters
67
+ ----------
68
+ e : dict
69
+ Returns
70
+ -------
71
+ node : dict
72
+ """
73
+
74
+ uninteresting_tags = {
75
+ 'source',
76
+ 'source_ref',
77
+ 'source:ref',
78
+ 'history',
79
+ 'attribution',
80
+ 'created_by',
81
+ 'tiger:tlid',
82
+ 'tiger:upload_uuid',
83
+ }
84
+
85
+ node = {
86
+ 'id': e['id'],
87
+ 'lat': e['lat'],
88
+ 'lon': e['lon']
89
+ }
90
+
91
+ if 'tags' in e:
92
+ for t, v in list(e['tags'].items()):
93
+ if t not in uninteresting_tags:
94
+ node[t] = v
95
+
96
+ return node
97
+
98
+
99
+ def make_osm_query(query):
100
+ """
101
+ Make a request to OSM and return the parsed JSON.
102
+
103
+ Parameters
104
+ ----------
105
+ query : str
106
+ A string in the Overpass QL format.
107
+
108
+ Returns
109
+ -------
110
+ data : dict
111
+
112
+ """
113
+ osm_url = 'http://www.overpass-api.de/api/interpreter'
114
+ req = requests.get(osm_url, params={'data': query})
115
+ req.raise_for_status()
116
+
117
+ return req.json()
118
+
119
+
120
+ def build_node_query(lat_min, lng_min, lat_max, lng_max, tags=None):
121
+ """
122
+ Build the string for a node-based OSM query.
123
+
124
+ Parameters
125
+ ----------
126
+ lat_min, lng_min, lat_max, lng_max : float
127
+ tags : str or list of str, optional
128
+ Node tags that will be used to filter the search.
129
+ See http://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide
130
+ for information about OSM Overpass queries
131
+ and http://wiki.openstreetmap.org/wiki/Map_Features
132
+ for a list of tags.
133
+
134
+ Returns
135
+ -------
136
+ query : str
137
+
138
+ """
139
+ if tags is not None:
140
+ if isinstance(tags, str):
141
+ tags = [tags]
142
+ tags = ''.join('[{}]'.format(t) for t in tags)
143
+ else:
144
+ tags = ''
145
+
146
+ query_fmt = (
147
+ '[out:json];'
148
+ '('
149
+ ' node'
150
+ ' {tags}'
151
+ ' ({lat_min},{lng_min},{lat_max},{lng_max});'
152
+ ');'
153
+ 'out;')
154
+
155
+ return query_fmt.format(
156
+ lat_min=lat_min, lng_min=lng_min, lat_max=lat_max, lng_max=lng_max,
157
+ tags=tags)
158
+
159
+
160
+ def node_query(lat_min, lng_min, lat_max, lng_max, tags=None):
161
+ """
162
+ Search for OSM nodes within a bounding box that match given tags.
163
+
164
+ Parameters
165
+ ----------
166
+ lat_min, lng_min, lat_max, lng_max : float
167
+ tags : str or list of str, optional
168
+ Node tags that will be used to filter the search.
169
+ See http://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide
170
+ for information about OSM Overpass queries
171
+ and http://wiki.openstreetmap.org/wiki/Map_Features
172
+ for a list of tags.
173
+
174
+ Returns
175
+ -------
176
+ nodes : pandas.DataFrame
177
+ Will have 'lat' and 'lon' columns, plus other columns for the
178
+ tags associated with the node (these will vary based on the query).
179
+ Index will be the OSM node IDs.
180
+
181
+ """
182
+ node_data = make_osm_query(build_node_query(
183
+ lat_min, lng_min, lat_max, lng_max, tags=tags))
184
+
185
+ if len(node_data['elements']) == 0:
186
+ raise RuntimeError('OSM query results contain no data.')
187
+
188
+ nodes = [process_node(n) for n in node_data['elements']]
189
+ return pd.DataFrame.from_records(nodes, index='id')