[Tutorial] - Using WMS Map Provider with ModestMaps (epsg:4236 and epsg:900913).
There has been many people here trying to implement a WMS provider (including me ^^) and i finally worked it out using MM map providers so i thought i would share it.
First of all, download the Modest Map library from SVN, not the SWC from the home page, its much much better :).
Then create a new class exactly like OpenStreetMapProvider (same signatures, same imports, same methods) and add two member variables:
private var serverUrl:String = "http://maps.geog.umd.edu/wmsconnector/com.esri.wms.Esrimap";
private var wms:String = "?LAYERS=0,1&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&PORT=1200&BBOX=";
if you are using a 4236 projection, add a different projection to your constructor:
var t:Transformation = new Transformation(166886.05360752725, 0, 524288,
0, -166886.05360752725, 524288);
__projection = new LinearProjection(20, t);
If you are using a 900913 projection, don't change anything it should be working fine as google mercator is the basic projection in MM.
For the getTileUrls method, write:
And in Location.as, add the following method (its just to invert lat/lon in a clean way):
I work with Flex Builder but you can easily adapt this. Create an ActionScript Project, copy the library and add your provider (i called it TestWMSProvider).
In your main application file, just put:
And this is what you will get !
I also worked out how to load some TileCache (local profile) with any projection (inc. not 4236 and 900913) but it's very specific and still a bit bugged ^^
Hope this helps!
First of all, download the Modest Map library from SVN, not the SWC from the home page, its much much better :).
Then create a new class exactly like OpenStreetMapProvider (same signatures, same imports, same methods) and add two member variables:
private var serverUrl:String = "http://maps.geog.umd.edu/wmsconnector/com.esri.wms.Esrimap";
private var wms:String = "?LAYERS=0,1&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&PORT=1200&BBOX=";
if you are using a 4236 projection, add a different projection to your constructor:
var t:Transformation = new Transformation(166886.05360752725, 0, 524288,
0, -166886.05360752725, 524288);
__projection = new LinearProjection(20, t);
If you are using a 900913 projection, don't change anything it should be working fine as google mercator is the basic projection in MM.
For the getTileUrls method, write:
public function getTileUrls(coord:Coordinate):Array
{
var boundingBox:String = "";
var sourceCoord:Coordinate = sourceCoordinate(coord);
var leftBottomCoord:Location = coordinateLocation(coord.down());
var topRightCoord:Location = coordinateLocation(coord.right());
boundingBox = leftBottomCoord.toWMSString()+ "," + topRightCoord.toWMSString();
trace ("boundingBox: "+serverUrl + wms + boundingBox);
return [serverUrl + wms + boundingBox];
}
And in Location.as, add the following method (its just to invert lat/lon in a clean way):
public function toWMSString(precision:int=5):String
{
return [lon.toFixed(precision), lat.toFixed(precision)].join(',');
}
I work with Flex Builder but you can easily adapt this. Create an ActionScript Project, copy the library and add your provider (i called it TestWMSProvider).
In your main application file, just put:
package {
import com.modestmaps.Map;
import com.modestmaps.TweenMap;
import com.modestmaps.extras.MapControls;
import com.modestmaps.mapproviders.TestWMSProvider;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
public class TutorialWMSModestMap extends Sprite
{
public var map:Map;
public function TutorialWMSModestMap()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
map = new TweenMap(stage.stageWidth, stage.stageHeight, true, new TestWMSProvider());
addChild(map);
addChild(new MapControls(map));
}
}
}
And this is what you will get !
I also worked out how to load some TileCache (local profile) with any projection (inc. not 4236 and 900913) but it's very specific and still a bit bugged ^^
Hope this helps!
3
people like this idea
I like this idea!
Tell me when this idea gets some attention.
The more people who like this idea, the more it gets noticed.
The more people who like this idea, the more it gets noticed.
The company implemented this idea.
The best point from the company
-
See the footer of http://modestmaps.mapstraction.com/tr... for an immediate download, or stay tuned for an update to the downloadable archives on modestmaps.com
The company thinks
this is one of the best points
Create a customer community for your own organization
Plans starting at $19/month
-
Inappropriate?Nice! Thanks for taking the time to write that up.
It seems fairly straight forward to make this into a general purpose class. If that would be useful I could add it to the source repository?
It would look something like this:
/**
* MapProvider for a WMS server, in either epsg:4326 or epsg:900913
*/
package com.modestmaps.mapproviders
{
import com.modestmaps.core.Coordinate;
import com.modestmaps.geo.LinearProjection;
import com.modestmaps.geo.Location;
import com.modestmaps.geo.Transformation;
import flash.net.URLVariables;
public class WMSMapProvider
extends AbstractMapProvider
implements IMapProvider
{
public static const EPSG_4326:String = "EPSG:4326";
public static const EPSG_900913:String = "EPSG:900913";
public static const DEFAULT_PARAMS:Object = {
LAYERS: '0,1',
FORMAT: 'image/png',
VERSION: '1.1.1',
SERVICE: 'WMS',
REQUEST: 'GetMap',
SRS: 'EPSG:4326',
WIDTH: '256',
HEIGHT: '256',
PORT: '1200'
};
private var serverUrl:String;
private var wms:String;
public function WMSMapProvider(serverURL:String, wmsParams:Object=null)
{
super();
this.serverUrl = serverURL;
if (!wmsParams) wmsParams = DEFAULT_PARAMS;
var data:URLVariables = new URLVariables();
for (var param:String in wmsParams) {
data[param] = wmsParams[param];
}
this.wms = "?" + data.toString();
if (wmsParams['SRS'] == EPSG_4326) {
var t:Transformation = new Transformation(166886.05360752725, 0, 524288, 0, -166886.05360752725, 524288);
__projection = new LinearProjection(20, t);
}
else if (wmsParams['SRS'] != EPSG_900913) {
throw new Error("[WMSMapProvider] Only Linear and (Google-style) Mercator projections are currently supported");
}
}
public function getTileUrls(coord:Coordinate):Array
{
var sourceCoord:Coordinate = sourceCoordinate(coord);
var bottomLeft:Location = coordinateLocation(coord.down());
var topRight:Location = coordinateLocation(coord.right());
var boundingBox:String = '&BBOX=' + [ bottomLeft.lon.toFixed(5), bottomLeft.lat.toFixed(5), topRight.lon.toFixed(5), topRight.lat.toFixed(5) ].join(',')
//trace ("boundingBox: "+serverUrl + wms + boundingBox);
return [serverUrl + wms + boundingBox];
}
public function toString() : String
{
return "WMS";
}
}
}
And then you would initialize it like this:
package
{
import com.modestmaps.Map;
import com.modestmaps.TweenMap;
import com.modestmaps.extras.MapControls;
import com.modestmaps.mapproviders.IMapProvider;
import com.modestmaps.mapproviders.WMSMapProvider;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class wms_test extends Sprite
{
public var map:Map;
public function wms_test()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
var baseURL:String = "http://maps.geog.umd.edu/wmsconnector/com.esri.wms.Esrimap";
var wmsProvider:IMapProvider = new WMSMapProvider(baseURL, WMSMapProvider.DEFAULT_PARAMS);
map = new TweenMap(stage.stageWidth, stage.stageHeight, true, wmsProvider);
addChild(map);
addChild(new MapControls(map));
stage.addEventListener(Event.RESIZE, onStageResize);
}
public function onStageResize(event:Event):void
{
map.setSize(stage.stageWidth, stage.stageHeight);
}
}
}
I’m thankful
-
Inappropriate?Sounds great, thanks for rewriting it in a clean way ^^. Would be cool to add it to the repository :).
PS: The port argument is not necessary actually, its just some old copy/paste.
I’m happy
-
Inappropriate?I added this to subversion:
http://modestmaps.mapstraction.com/tr...
Do you know of any other public WMS endpoints we could test with? -
Inappropriate?Cool :D
i got this wms list i've been testing with (randomly):
// serveur geosignal
//private var serverUrl:String = "http://www.geosignal.org/cgi-bin/wmsmap";
//private var wms:String = "?LAYERS=Nationales&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326&WIDTH=256&HEIGHT=256&BBOX=";
// serveur fire information
//private var serverUrl:String = "http://maps.geog.umd.edu/wmsconnector/com.esri.wms.Esrimap";
//private var wms:String = "?LAYERS=0,1&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&PORT=1200&BBOX=";
// ecogis (suisse)
//private var serverUrl:String = "http://www.ecogis.admin.ch/fr/wms";
//private var wms:String = "?LAYERS=chseen&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&BBOX=";
// canada birds (déformé)
//private var serverUrl:String = "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp";
//private var wms:String = "?LAYERS=IBA&FORMAT=image%2Fpng&ServiceName=ESRI_World&WMTVER=1.0.0&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&BBOX=";
// points sur l'inde. Server list http://www.who.int/tools/geoserver/ma...
//private var serverUrl:String = "http://www.who.int/tools/geoserver/wms";
//private const wms:String = "?LAYERS=WHO:mm_ppoint&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&EPSG:900913&WIDTH=256&HEIGHT=256&PORT=1200&BBOX=";
//private var wms:String = "?LAYERS=WHO:mm_ppoint&FORMAT=image%2Fpng&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&EPSG:900913&WIDTH=256&HEIGHT=256&PORT=1200&BBOX=";
// cubewerx
//private var serverUrl:String = "http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi";
//private var wms:String = "?LAYERS=BATIMEN_A:nrcan&FORMAT=image%2Fpng&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:900913&WIDTH=256&HEIGHT=256&BBOX=";
// canada
private var serverUrl:String = "http://wms.geobase.ca/wms-bin/cubeserv.cgi";
private var wms:String = "?LAYERS=DNEC_250K:ELEVATION/ELEVATION&FORMAT=image%2Fpng&SERVICE=WMS&REQUEST=GetMap&SRS=EPSG:4326&WIDTH=256&HEIGHT=256&BBOX=";
// tst As3maplib
//private var serverUrl:String = "http://labs.metacarta.com/wms/vmap0";
//private var wms:String = "?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=image%2Fjpeg&WIDTH=256&HEIGHT=256&BBOX=";
If you want some more, you can look for "wms getcapabilities" over google and u'll get all the xml description files with layer/server lists
-
Inappropriate?Thanks for the list, that's very helpful.
I'm surprised you hadn't already written a reusable class for this ;) I guess a WMSCapabilities class would be a useful thing, to load the capabilities xml and instantiate WMSMapProviders for each layer?
I haven't found a WMS server with crossdomain.xml permissions yet though, so it wouldn't be possible to have a demo online without a server-side proxy on the same domain. That means it's not something I'm personally interested in writing right now, but if someone else wants to take a crack at it I'd be happy to help test.
I’m thankful
-
Inappropriate?Well this is just a simplified sample from what i've written so far. A colleague of mine has written a class to parse the GetCapabilities XML but i didn't have time to review it yet.
The openlayers WMS server has a crossdomain file:
http://labs.metacarta.com/crossdomain...
But i don't know what this site-control tag is about so idk if it would work ok.
At the moment, i'm more focused on trying to make ModestMaps work with TileCache. It's a bit tricky cause there are many many differents things compared to the usual map providers. Like the map doesn't have to be square, scales are not a power of 2 (can be like 10K, 5K and then 2K, not 2.5K), the origin is the bottom left corner, the tiles can be bigger than the bounding box and the map can be in any projection so i had to rewrite a lot of code to make it work properly. You have to read and parse an XML as well to get the scales values.
I pretty much made it work so far (i can set an extent and zoom box), there is still the "double clic zoom" that i need to work out but its a mess with these scales that aren't a power of 2 :P
I'll post some more maybe when it will be clean enough :) -
I think the site-control setting in that crossdomain.xml file means that sub-directories on labs.metacarta.com aren't allowed to override the crossdomain settings. It should work fine. -
Inappropriate?I'm curious to hear how you were able to deal with the powers-of-two thing from TileCache, fnicollet. It's such a core assumption of Modest Maps that I'd essentially written off support for such providers for the near future. Do you have any code to share?
-
Inappropriate?Well right now the power of 2 thing isn't working, i just totally changed the setExtent method to calculate the centerCoord from the informations coming from the XML (origin/scales), which is already a bit of a mess. So i determine which zoom level is the most appropriate and calculate the centerCoord row and column from the origin and the resolutions.
And to determine the number of tiles in a row or in a column, you can't use zoom*zoom because unlike world-maps, they may not be square so u have to calculate it all the time (but i think i will just pre-calculate them when loading).
This way, i can use the zoomBox (bit rewritten too) and set the initial extent.
As u said, the power-of-2 is a really core feature so there may be too much stuff to rewrite and i may end-up just calculating the extent manually and setExtent with it or something.
i'll keep u posted :) -
Inappropriate?I've just realised that although I checked in code that claimed to work with Mercator WMS maps, I didn't actually successfully test with one.
I've just made the necessary adjustments so that it will generate a BBOX in the correct coordinate space. Hopefully this will help those of you who want such things to work out of the box :)
For those following along in svn, the changeset is here:
http://modestmaps.mapstraction.com/tr...
It seems to do the right thing with the openlayers vmap wms, initialised like so:
new WMSMapProvider("http://labs.metacarta.com/wms/vmap0",
{
LAYERS: 'basic',
SERVICE: 'WMS',
VERSION: '1.1.1',
REQUEST: 'GetMap',
STYLES: '',
SRS: WMSMapProvider.EPSG_900913,
EXCEPTIONS: 'application/vnd.ogc.se_inimage',
FORMAT: 'image/jpeg',
WIDTH: '256',
HEIGHT: '256'
});
If anyone has a WMS server that's publicly available and requires extra features (optional arguments, different projections etc) I'm happy to help extend this code further, either on this thread or a new one.
I’m undecided
1 person thinks
this is one of the best points
-
Inappropriate?See the footer of http://modestmaps.mapstraction.com/tr... for an immediate download, or stay tuned for an update to the downloadable archives on modestmaps.com
The company thinks
this is one of the best points
Loading Profile...



EMPLOYEE
EMPLOYEE