Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added WMS Provider, added support for POST data. #1

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions ModestMaps/Providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ def __init__(self):
def getTileUrls(self, coordinate):
raise NotImplementedError("Abstract method not implemented by subclass.")

def getTileUrls(self, coordinate):
raise NotImplementedError("Abstract method not implemented by subclass.")

def tileWidth(self):
raise NotImplementedError("Abstract method not implemented by subclass.")

Expand All @@ -31,9 +28,6 @@ def locationCoordinate(self, location):
def coordinateLocation(self, location):
return self.projection.coordinateLocation(location)

def sourceCoordinate(self, coordinate):
raise NotImplementedError("Abstract method not implemented by subclass.")

def sourceCoordinate(self, coordinate):
wrappedColumn = coordinate.column % pow(2, coordinate.zoom)

Expand All @@ -42,6 +36,9 @@ def sourceCoordinate(self, coordinate):

return Coordinate(coordinate.row, wrappedColumn, coordinate.zoom)

def getPostData(self):
return None

class TemplatedMercatorProvider(IMapProvider):
""" Convert URI templates into tile URLs, using a tileUrlTemplate identical to:
http://code.google.com/apis/maps/documentation/overlays.html#Custom_Map_Types
Expand Down
88 changes: 88 additions & 0 deletions ModestMaps/WMS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""
>>> w = Provider('http://localhost:8080/geoserver/gwc/service/wms')
>>> w.server
'http://localhost:8080/geoserver/wms'
>>> w.wms
'?LAYERS=0%2C1&SERVICE=WMS&WIDTH=256&FORMAT=image%2Fpng&REQUEST=GetMap&HEIGHT=256&SRS=EPSG%3A4326&VERSION=1.1.1'
>>> w.getTileUrls(Coordinate(0, 0, 20))
['http://localhost:8080/geoserver/wms?LAYERS=0%2C1&SERVICE=WMS&WIDTH=256&FORMAT=image%2Fpng&REQUEST=GetMap&HEIGHT=256&SRS=EPSG%3A4326&VERSION=1.1.1&BBOX=-180.000000,180.021231,-179.999657,180.021574']
"""

from Core import Coordinate
from Providers import IMapProvider
from Geo import Transformation, LinearProjection, MercatorProjection
from urllib import urlencode
from copy import copy
import math

class Provider(IMapProvider):
_PARAMS = {
'LAYERS': '0,1',
'FORMAT': 'image/png',
'VERSION': '1.1.1',
'SERVICE': 'WMS',
'REQUEST': 'GetMap',
'SRS': 'EPSG:4326',
'WIDTH': 256,
'HEIGHT': 256
}

def __init__(self, server, params=None):
self.params = copy(Provider._PARAMS)

if not params is None:
self.params.update(params)

if 'SLD_BODY' in self.params:
self.postData = self.params['SLD_BODY']
del self.params['SLD_BODY']
else:
self.postData = None

self.server = server.replace('gwc/service/','')

self.wms = '?' + urlencode(self.params)

if 'SRS' in self.params and self.params['SRS'] == 'EPSG:4326':
self.projection = LinearProjection(20, Transformation(166886.05360752725, 0, 524288, 0, -166866.05360752725, 524288))
else:
self.projection = MercatorProjection(26, Transformation(1.068070779e7, 0, 3.355443185e7, 0, -1.068070890e7, 3.355443057e7))

def getTileUrls(self, coord):
sourceCoord = self.sourceCoordinate(coord)
ll = sourceCoord.down()
ur = sourceCoord.right()

bbox = ''
if 'SRS' in self.params and self.params['SRS'] == 'EPSG:4326':
ll_loc = self.coordinateLocation(ll)
ur_loc = self.coordinateLocation(ur)
bbox = '&BBOX=%f,%f,%f,%f' % (ll_loc.lon, ll_loc.lat, ur_loc.lon, ur_loc.lat)
else:
qwidth = 20037508.34;
mzoom = math.log(2*qwidth) / math.log(2)

ll = ll.zoomTo(mzoom)
ur = ur.zoomTo(mzoom)

bbox = '&BBOX=%f,%f,%f,%f' % (
ll.column - qwidth,
qwidth - ll.row,
ur.column - qwidth,
qwidth - ur.row
)

return ['%s%s%s' % (self.server, self.wms, bbox)]

def tileWidth(self):
return self.params['WIDTH']

def tileHeight(self):
return self.params['HEIGHT']

def getPostData(self):
return self.postData

if __name__ == '__main__':
import doctest
doctest.testmod()
17 changes: 13 additions & 4 deletions ModestMaps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
import math
import thread
import time
import traceback

try:
import PIL.Image
Expand All @@ -82,7 +83,7 @@
import Providers
import Core
import Geo
import Yahoo, Microsoft, BlueMarble, OpenStreetMap, CloudMade
import Yahoo, Microsoft, BlueMarble, OpenStreetMap, CloudMade, WMS
import time

# a handy list of possible providers, which isn't
Expand All @@ -102,7 +103,8 @@
'CLOUDMADE_TOURIST': CloudMade.TouristProvider,
'CLOUDMADE_FRESH': CloudMade.FreshProvider,
'CLOUDMADE_PALEDAWN': CloudMade.PaleDawnProvider,
'CLOUDMADE_MIDNIGHTCOMMANDER': CloudMade.MidnightCommanderProvider
'CLOUDMADE_MIDNIGHTCOMMANDER': CloudMade.MidnightCommanderProvider,
'WMS': WMS.Provider
}

def mapByCenterZoom(provider, center, zoom, dimensions):
Expand Down Expand Up @@ -228,6 +230,10 @@ def load(self, lock, verbose, attempt=1):
return

urls = self.provider.getTileUrls(self.coord)

method = 'GET'
if not self.provider.getPostData() is None:
method = 'POST'

if verbose:
print 'Requesting', urls, '- attempt no.', attempt, 'in thread', hex(thread.get_ident())
Expand All @@ -238,7 +244,10 @@ def load(self, lock, verbose, attempt=1):

for (scheme, netloc, path, params, query, fragment) in map(urlparse.urlparse, urls):
conn = httplib.HTTPConnection(netloc)
conn.request('GET', path + ('?' + query).rstrip('?'), headers={'User-Agent': 'Modest Maps python branch (http://modestmaps.com)'})

body = self.provider.getPostData()
conn.request(method, path + ('?' + query).rstrip('?'), body=body, headers={'User-Agent': 'Modest Maps python branch (http://modestmaps.com)'})

response = conn.getresponse()

if str(response.status).startswith('2'):
Expand Down Expand Up @@ -474,7 +483,7 @@ def render_tiles(self, tiles, img_width, img_height, verbose=False):
# hang around until they are loaded or we run out of time...
time.sleep(1)

mapImg = PIL.Image.new('RGB', (img_width, img_height))
mapImg = PIL.Image.new('RGBA', (img_width, img_height))

for tile in tiles:
try:
Expand Down
142 changes: 142 additions & 0 deletions wms_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/python
import ModestMaps

provider = ModestMaps.WMS.Provider('http://gmu.azavea.com/geoserver/wms',{
'LAYERS':'pmp:demo_va_county_poptot',
'WIDTH': 256,
'HEIGHT': 256,
'SRS':'EPSG:3785'
})

ll = ModestMaps.Geo.Location(36.483530296000694, -83.781734299520878)
ur = ModestMaps.Geo.Location(39.504037835739027, -75.124508209709902)
dims = ModestMaps.Core.Point(512,226)

image = ModestMaps.mapByExtent(provider, ll, ur, dims).draw()
image.save('regular.png')

sld_body = """<?xml version="1.0" encoding="ISO-8859-1"?>
<sld:StyledLayerDescriptor version="1.0.0" xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd">
<sld:NamedLayer>
<sld:Name>pmp:demo_va_county_poptot</sld:Name>
<sld:UserStyle>
<sld:Name>Default Styler</sld:Name>
<sld:Title>Population</sld:Title>
<sld:Abstract>A grayscale style showing the population numbers in a given geounit.</sld:Abstract>
<sld:FeatureTypeStyle>
<sld:Name>name</sld:Name>
<sld:Rule>
<sld:Title>&lt; 48,350</sld:Title>
<ogc:Filter>
<ogc:PropertyIsLessThan>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>48350</ogc:Literal>
</ogc:PropertyIsLessThan>
</ogc:Filter>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParameter name="fill">#252525</sld:CssParameter>
</sld:Fill>
</sld:PolygonSymbolizer>
</sld:Rule>
<sld:Rule>
<sld:Title>&gt; 48,350</sld:Title>
<ogc:Filter>
<ogc:And>
<ogc:PropertyIsLessThan>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>128000</ogc:Literal>
</ogc:PropertyIsLessThan>
<ogc:PropertyIsGreaterThanOrEqualTo>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>48350</ogc:Literal>
</ogc:PropertyIsGreaterThanOrEqualTo>
</ogc:And>
</ogc:Filter>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParameter name="fill">#636363</sld:CssParameter>
</sld:Fill>
</sld:PolygonSymbolizer>
</sld:Rule>
<sld:Rule>
<sld:Title>&gt; 128,000</sld:Title>
<ogc:Filter>
<ogc:And>
<ogc:PropertyIsLessThan>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>237000</ogc:Literal>
</ogc:PropertyIsLessThan>
<ogc:PropertyIsGreaterThanOrEqualTo>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>128000</ogc:Literal>
</ogc:PropertyIsGreaterThanOrEqualTo>
</ogc:And>
</ogc:Filter>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParameter name="fill">#969696</sld:CssParameter>
</sld:Fill>
</sld:PolygonSymbolizer>
</sld:Rule>
<sld:Rule>
<sld:Title>&gt; 237,000</sld:Title>
<ogc:Filter>
<ogc:And>
<ogc:PropertyIsLessThan>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>447800</ogc:Literal>
</ogc:PropertyIsLessThan>
<ogc:PropertyIsGreaterThanOrEqualTo>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>237000</ogc:Literal>
</ogc:PropertyIsGreaterThanOrEqualTo>
</ogc:And>
</ogc:Filter>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParameter name="fill">#CCCCCC</sld:CssParameter>
</sld:Fill>
</sld:PolygonSymbolizer>
</sld:Rule>
<sld:Rule>
<sld:Title>&gt; 447,800</sld:Title>
<ogc:Filter>
<ogc:PropertyIsGreaterThanOrEqualTo>
<ogc:PropertyName>number</ogc:PropertyName>
<ogc:Literal>447800</ogc:Literal>
</ogc:PropertyIsGreaterThanOrEqualTo>
</ogc:Filter>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParameter name="fill">#F7F7F7</sld:CssParameter>
</sld:Fill>
</sld:PolygonSymbolizer>
</sld:Rule>
<sld:Rule>
<sld:Title>Boundary</sld:Title>
<sld:LineSymbolizer>
<sld:Stroke>
<sld:CssParameter name="stroke">#202020</sld:CssParameter>
<sld:CssParameter name="stroke-width">0.50</sld:CssParameter>
</sld:Stroke>
</sld:LineSymbolizer>
</sld:Rule>
</sld:FeatureTypeStyle>
</sld:UserStyle>
</sld:NamedLayer>
</sld:StyledLayerDescriptor>
"""

provider = ModestMaps.WMS.Provider('http://gmu.azavea.com/geoserver/wms',{
'LAYERS':'pmp:demo_va_county_poptot',
'WIDTH': 256,
'HEIGHT': 256,
'SRS':'EPSG:3785',
'SLD_BODY': sld_body
})

image = ModestMaps.mapByExtent(provider, ll, ur, dims).draw()
image.save('invert.png')