Skip to content

Commit a818003

Browse files
authored
Merge pull request #94 from developmentseed/feature/93-wms
Feature/93 wms
2 parents 569a3f0 + adc7eb1 commit a818003

File tree

10 files changed

+71
-13
lines changed

10 files changed

+71
-13
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ Before running any commands, it is necessary to create a `config.json` file to s
4747
- `imagery`: One of:
4848
- A template string for a tiled imagery service. Note that you will generally need an API key to obtain images and there may be associated costs. The above example requires a [Mapbox access token](https://www.mapbox.com/help/how-access-tokens-work/)
4949
- A GeoTIFF file location. Works with both local and remote files. Ex: `'http://oin-hotosm.s3.amazonaws.com/593ede5ee407d70011386139/0/3041615b-2bdb-40c5-b834-36f580baca29.tif'`
50+
- A [WMS endpoint](http://www.opengeospatial.org/standards/wms) `GetMap` request. Fill out all necessary parameters except `bbox` which should be set as `{bbox}`. Ex:
51+
`'https://basemap.nationalmap.gov/arcgis/services/USGSImageryOnly/MapServer/WMSServer?SERVICE=WMS&REQUEST=GetMap&VERSION=1.1.1&LAYERS=0&STYLES=&FORMAT=image%2Fjpeg&TRANSPARENT=false&HEIGHT=256&WIDTH=256&SRS=EPSG%3A3857&BBOX={bbox}'`
5052
- `background_ratio`: For single-class classification problems, we need to download images with no matching class. We will download `background_ratio` times the number of images matching the one class.
5153
- `ml_type`: One of `"classification"`, `"object-detection"`, or `"segmentation"`. For the final label numpy arrays (`y_train` and `y_test`), we will produce a different label depending upon the `type`.
5254
- `"classification"`: An array of the same length as `classes`. Each array value will be either `1` or `0` based on whether it matches the class at the same index

label_maker/countries.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ south_georgia_and_south_sandwich_islands
197197
south_korea_korea
198198
south_sudan
199199
spain
200-
sri_lanka_lanka
200+
sri_lanka
201201
sudan
202202
suriname
203203
swaziland

label_maker/images.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import numpy as np
88

9-
from label_maker.utils import download_tile_tms, get_tile_tif, is_tif
9+
from label_maker.utils import get_image_function
1010

1111
def download_images(dest_folder, classes, imagery, ml_type, background_ratio, imagery_offset=False, **kwargs):
1212
"""Download satellite images specified by a URL and a label.npz file
@@ -69,9 +69,7 @@ def class_test(value):
6969
print('Downloading {} tiles to {}'.format(len(tiles), tiles_dir))
7070

7171
# get image acquisition function based on imagery string
72-
image_function = download_tile_tms
73-
if is_tif(imagery):
74-
image_function = get_tile_tif
72+
image_function = get_image_function(imagery)
7573

7674
for tile in tiles:
7775
image_function(tile, imagery, tiles_dir, imagery_offset)

label_maker/preview.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import numpy as np
88
from PIL import Image, ImageDraw
99

10-
from label_maker.utils import class_match, download_tile_tms, get_tile_tif, is_tif
10+
from label_maker.utils import class_match, get_image_function
1111

1212
def preview(dest_folder, number, classes, imagery, ml_type, imagery_offset=False, **kwargs):
1313
"""Produce imagery examples for specified classes
@@ -48,9 +48,7 @@ def preview(dest_folder, number, classes, imagery, ml_type, imagery_offset=False
4848
print('Writing example images to {}'.format(examples_dir))
4949

5050
# get image acquisition function based on imagery string
51-
image_function = download_tile_tms
52-
if is_tif(imagery):
53-
image_function = get_tile_tif
51+
image_function = get_image_function(imagery)
5452

5553
for i, cl in enumerate(classes):
5654
# create class directory

label_maker/utils.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# pylint: disable=unused-argument
22
"""Provide utility functions"""
33
from os import path as op
4-
from urllib.parse import urlparse
4+
from urllib.parse import urlparse, parse_qs
55

66
from mercantile import bounds
77
from pyproj import Proj, transform
@@ -30,7 +30,8 @@ def download_tile_tms(tile, imagery, folder, *args):
3030
_, image_format = op.splitext(o.path)
3131
r = requests.get(url(tile.split('-'), imagery))
3232
tile_img = op.join(folder, '{}{}'.format(tile, image_format))
33-
open(tile_img, 'wb').write(r.content)
33+
with open(tile_img, 'wb')as w:
34+
w.write(r.content)
3435
return tile_img
3536

3637
def get_tile_tif(tile, imagery, folder, imagery_offset):
@@ -86,6 +87,49 @@ def get_tile_tif(tile, imagery, folder, imagery_offset):
8687

8788
return tile_img
8889

90+
def get_tile_wms(tile, imagery, folder, imagery_offset):
91+
"""
92+
Read a WMS endpoint with query parameters corresponding to a TMS tile
93+
94+
Converts the tile boundaries to the spatial reference system (SRS) specified
95+
by the WMS query parameter.
96+
"""
97+
# retrieve the necessary parameters from the query string
98+
query_dict = parse_qs(imagery.lower())
99+
image_format = query_dict['format'][0].split('/')[1]
100+
wms_srs = query_dict['srs'][0]
101+
102+
# find our tile bounding box
103+
bound = bounds(*[int(t) for t in tile.split('-')])
104+
p1 = Proj({'init': 'epsg:4326'})
105+
p2 = Proj({'init': wms_srs})
106+
107+
# project the tile bounding box from lat/lng to WMS SRS
108+
tile_ll_proj = transform(p1, p2, bound.west, bound.south)
109+
tile_ur_proj = transform(p1, p2, bound.east, bound.north)
110+
bbox = tile_ll_proj + tile_ur_proj
111+
112+
# request the image with the transformed bounding box and save
113+
wms_url = imagery.replace('{bbox}', ','.join([str(b) for b in bbox]))
114+
r = requests.get(wms_url)
115+
tile_img = op.join(folder, '{}.{}'.format(tile, image_format))
116+
with open(tile_img, 'wb') as w:
117+
w.write(r.content)
118+
return tile_img
119+
120+
89121
def is_tif(imagery):
90122
"""Determine if an imagery path has a valid tif extension"""
91123
return op.splitext(imagery)[1].lower() in ['.tif', '.tiff', '.vrt']
124+
125+
def is_wms(imagery):
126+
"""Determine if an imagery path is a WMS endpoint"""
127+
return '{bbox}' in imagery
128+
129+
def get_image_function(imagery):
130+
"""Return the correct image downloading function based on the imagery string"""
131+
if is_tif(imagery):
132+
return get_tile_tif
133+
if is_wms(imagery):
134+
return get_tile_wms
135+
return download_tile_tms

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
astroid>=1.6.0
22
isort>=4.2.15
3-
pylint>=1.8.1
3+
pylint==1.8.1

test/fixtures/4686-6267-14.jpeg

36.1 KB
Loading

test/tiles/1087767-1046604-21.jpg

14.3 KB
Loading

test/tiles/4686-6267-14.jpeg

36.1 KB
Loading

test/unit/test_utils.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import numpy as np
66
from PIL import Image
77

8-
from label_maker.utils import url, class_match, get_tile_tif
8+
from label_maker.utils import url, class_match, get_tile_tif, get_tile_wms
99

1010
class TestUtils(unittest.TestCase):
1111
"""Tests for utility functions"""
@@ -85,5 +85,21 @@ def test_get_tile_vrt(self):
8585
fixture_tile = Image.open('test/fixtures/{}.jpg'.format(tile))
8686
self.assertEqual(test_tile, fixture_tile)
8787

88+
def test_get_tile_wms(self):
89+
"""Test reading of tile from a WMS endpoint"""
90+
tile = '4686-6267-14'
91+
# create tiles directory
92+
dest_folder = 'test'
93+
tiles_dir = op.join(dest_folder, 'tiles')
94+
if not op.isdir(tiles_dir):
95+
makedirs(tiles_dir)
96+
97+
usgs_url = 'https://basemap.nationalmap.gov/arcgis/services/USGSImageryOnly/MapServer/WMSServer?SERVICE=WMS&REQUEST=GetMap&VERSION=1.1.1&LAYERS=0&STYLES=&FORMAT=image%2Fjpeg&TRANSPARENT=false&HEIGHT=256&WIDTH=256&SRS=EPSG%3A3857&BBOX={bbox}'
98+
99+
get_tile_wms(tile, usgs_url, tiles_dir, None)
100+
test_tile = Image.open('test/tiles/{}.jpeg'.format(tile))
101+
fixture_tile = Image.open('test/fixtures/{}.jpeg'.format(tile))
102+
self.assertEqual(test_tile, fixture_tile)
103+
88104
if __name__ == '__main__':
89105
unittest.main()

0 commit comments

Comments
 (0)