Skip to content

Commit 7692de5

Browse files
authored
Merge pull request #30 from interline-io/polygon-extract
Osmium Polygon extracts
2 parents 71f8810 + d42c779 commit 7692de5

File tree

9 files changed

+247
-47
lines changed

9 files changed

+247
-47
lines changed

planetutils/bbox.py

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,55 @@
44
import os
55
import csv
66

7+
def flatcoords(coords, fc=None):
8+
if fc is None:
9+
fc = []
10+
try:
11+
coords[0][0] # check if iterable of iterables
12+
for c in coords:
13+
flatcoords(c, fc)
14+
except:
15+
fc.append(coords)
16+
return fc
17+
18+
class Feature(object):
19+
def __init__(self, properties=None, geometry=None, **kwargs):
20+
self.properties = properties or {}
21+
self.geometry = geometry or {}
22+
if not self.geometry:
23+
self.set_bbox([0.0, 0.0, 0.0, 0.0])
24+
25+
def bbox(self):
26+
gt = self.geometry.get('type')
27+
coords = self.geometry.get('coordinates', [])
28+
fc = flatcoords(coords)
29+
lons = [i[0] for i in fc]
30+
lats = [i[1] for i in fc]
31+
left, right = min(lons), max(lons)
32+
bottom, top = min(lats), max(lats)
33+
return validate_bbox([left, bottom, right, top])
34+
35+
def set_bbox(self, bbox):
36+
left, bottom, right, top = validate_bbox(bbox)
37+
self.geometry = {
38+
"type": "LineString",
39+
"coordinates": [
40+
[left, bottom],
41+
[right, top],
42+
]
43+
}
44+
45+
def is_rectangle(self):
46+
fc = flatcoords(self.geometry.get('coordinates', []))
47+
lons = set([i[0] for i in fc])
48+
lats = set([i[1] for i in fc])
49+
return len(lons) <= 2 and len(lats) <= 2
50+
51+
# act like [left, bottom, right, top]
52+
def __getitem__(self, item):
53+
return self.bbox()[item]
54+
55+
756
def validate_bbox(bbox):
857
left, bottom, right, top = map(float, bbox)
958
assert -180 <= left <= 180
@@ -14,10 +63,12 @@ def validate_bbox(bbox):
1463
assert right >= left
1564
return [left, bottom, right, top]
1665

17-
def bbox_string(bbox):
18-
return validate_bbox(bbox.split(','))
19-
20-
def load_bboxes_csv(csvpath):
66+
def load_feature_string(bbox):
67+
f = Feature()
68+
f.set_bbox(bbox.split(','))
69+
return f
70+
71+
def load_features_csv(csvpath):
2172
# bbox csv format:
2273
# name, left, bottom, right, top
2374
if not os.path.exists(csvpath):
@@ -26,31 +77,25 @@ def load_bboxes_csv(csvpath):
2677
with open(csvpath) as f:
2778
reader = csv.reader(f)
2879
for row in reader:
29-
bboxes[row[0]] = validate_bbox(row[1:])
80+
if len(row) != 5:
81+
raise Exception('5 columns required')
82+
f = Feature()
83+
f.set_bbox(row[1:])
84+
bboxes[row[0]] = f
3085
return bboxes
3186

32-
def load_bboxes_geojson(path):
87+
def load_features_geojson(path):
3388
if not os.path.exists(path):
3489
raise Exception('file does not exist: %s'%path)
3590
with open(path) as f:
3691
data = json.load(f)
37-
return feature_bboxes(data.get('features',[]))
38-
39-
def feature_bboxes(features):
92+
# check if this is a single feature
93+
if data.get('type') == 'FeatureCollection':
94+
features = data.get('features', [])
95+
else:
96+
features = [data]
4097
bboxes = {}
4198
for count,feature in enumerate(features):
4299
key = feature.get('properties',{}).get('id') or feature.get('id') or count
43-
bbox = feature_bbox(feature)
44-
bboxes[key] = bbox
100+
bboxes[key] = Feature(**feature)
45101
return bboxes
46-
47-
def feature_bbox(feature):
48-
g = feature.get('geometry',{})
49-
if g.get('type') != 'Polygon':
50-
raise Exception('Only Polygon geometries are supported')
51-
coords = g.get('coordinates',[])[0]
52-
lons = [i[0] for i in coords]
53-
lats = [i[1] for i in coords]
54-
left, right = min(lons), max(lons)
55-
bottom, top = min(lats), max(lats)
56-
return validate_bbox([left, bottom, right, top])

planetutils/elevation_tile_download.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sys
55

66
from . import log
7-
from .bbox import load_bboxes_csv, bbox_string
7+
from .bbox import load_features_csv, load_feature_string
88
from .elevation_tile_downloader import ElevationGeotiffDownloader, ElevationSkadiDownloader
99

1010
def main():
@@ -30,9 +30,9 @@ def main():
3030
sys.exit(1)
3131

3232
if args.csv:
33-
p.download_bboxes(load_bboxes_csv(args.csv))
33+
p.download_bboxes(load_features_csv(args.csv))
3434
elif args.bbox:
35-
p.download_bbox(bbox_string(args.bbox))
35+
p.download_bbox(load_feature_string(args.bbox))
3636
else:
3737
p.download_planet()
3838

planetutils/osm_extract_download.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import argparse
55

66
from . import log
7-
from .bbox import bbox_string, load_bboxes_csv
87
from .osm_extract_downloader import OsmExtractDownloader
98

109
def main():

planetutils/osm_planet_extract.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import argparse
44
from .planet import *
55
from . import bbox
6-
from .bbox import bbox_string, load_bboxes_csv
6+
from .bbox import load_feature_string, load_features_csv
77

88
def main():
99
parser = argparse.ArgumentParser()
@@ -33,11 +33,11 @@ def main():
3333

3434
bboxes = {}
3535
if args.csv:
36-
bboxes = bbox.load_bboxes_csv(args.csv)
36+
bboxes = bbox.load_features_csv(args.csv)
3737
elif args.geojson:
38-
bboxes = bbox.load_bboxes_geojson(args.geojson)
38+
bboxes = bbox.load_features_geojson(args.geojson)
3939
elif (args.bbox and args.name):
40-
bboxes[args.name] = bbox.bbox_string(args.bbox)
40+
bboxes[args.name] = bbox.load_feature_string(args.bbox)
4141
else:
4242
parser.error('must specify --csv, --geojson, or --bbox and --name')
4343

planetutils/planet.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
import tempfile
1212
import json
1313

14-
import boto3
15-
1614
from . import log
1715
from .bbox import validate_bbox
1816

17+
try:
18+
import boto3
19+
except ImportError:
20+
boto3 = None
21+
1922
class PlanetBase(object):
2023
def __init__(self, osmpath=None, grain='hour', changeset_url=None, osmosis_workdir=None):
2124
self.osmpath = osmpath
@@ -106,14 +109,18 @@ def extract_bbox(self, name, bbox, workers=1, outpath='.', **kw):
106109
class PlanetExtractorOsmium(PlanetExtractor):
107110
def extract_bboxes(self, bboxes, workers=1, outpath='.', strategy='complete_ways', **kw):
108111
extracts = []
109-
for name, bbox in bboxes.items():
110-
validate_bbox(bbox)
111-
left, bottom, right, top = bbox
112-
extracts.append({
112+
for name, bbox in bboxes.items():
113+
ext = {
113114
'output': '%s.osm.pbf'%name,
114115
'output_format': 'pbf',
115-
'bbox': {'left': left, 'right': right, 'top': top, 'bottom':bottom}
116-
})
116+
}
117+
if bbox.is_rectangle():
118+
left, bottom, right, top = bbox.bbox()
119+
ext['bbox'] = {'left': left, 'right': right, 'top': top, 'bottom':bottom}
120+
else:
121+
ftype = bbox.geometry.get('type', '').lower()
122+
ext[ftype] = bbox.geometry.get('coordinates', [])
123+
extracts.append(ext)
117124
config = {'directory': outpath, 'extracts': extracts}
118125
path = None
119126
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
@@ -159,10 +166,14 @@ def download_planet_latest(self, bucket=None, prefix=None, match=None):
159166
self._download(planet.bucket_name, planet.key)
160167

161168
def _download(self, bucket_name, key):
169+
if not boto3:
170+
raise Exception('please install boto3')
162171
s3 = boto3.client('s3')
163172
s3.download_file(bucket_name, key, self.osmpath)
164173

165174
def _get_planets(self, bucket, prefix, match):
175+
if not boto3:
176+
raise Exception('please install boto3')
166177
r = re.compile(match)
167178
s3 = boto3.resource('s3')
168179
s3bucket = s3.Bucket(bucket)

planetutils/tilepack_download.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import argparse
55

66
from . import log
7-
from .bbox import bbox_string, load_bboxes_csv
87
from .tilepack_downloader import TilepackDownloader
98

109
def main():

planetutils/tilepack_list.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import os
44
import argparse
55

6-
from .bbox import bbox_string, load_bboxes_csv
76
from .tilepack_downloader import TilepackDownloader
87

98
def main():

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
author_email='ian@interline.io',
1818
license='MIT',
1919
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
20-
install_requires=['future', 'requests','boto3'], #, 'osmium'
20+
install_requires=['future', 'requests'], #, 'osmium', 'boto3'
2121
tests_require=['nose'],
2222
test_suite = 'nose.collector',
2323
entry_points={

0 commit comments

Comments
 (0)