After more than 3 years it still amazes me how good is myVR technology for 3D maps on iOS. Watch Czech Republic DMT with CUZK orthophoto as base map and cadastral or geography WMS overlay:
Quick and dirty test of the WMS capabilities of the new MapBox-gl-js 0.5.2 API. First of all, yes ! it is possible to overlay (legacy) WMS over the vector WebGL rendered base map … however the way is not straightforward:
Code is documented to guide you through the process, few highlights:
// -- rutine originaly found in GlobalMercator.js, simplified // -- calculates spherical mercator coordinates from tile coordinates function tileBounds(tx, ty, zoom, tileSize) { function pixelsToMeters(px, py, zoom) { var res = (2 * Math.PI * 6378137 / 256) / Math.pow(2, zoom), originShift = 2 * Math.PI * 6378137 / 2, x = px * res - originShift, y = py * res - originShift; return [Math.abs(x), Math.abs(y)]; }; var min = pixelsToMeters(tx * tileSize, ty * tileSize, zoom), max = pixelsToMeters((tx + 1) * tileSize, (ty + 1) * tileSize, zoom); return min.concat(max); }
] // -- save orig _loadTile function so we can call it later // -- there was no good pre-load event at mapbox API to get hooked and patch url // -- we need to use undocumented _loadTile var origFunc = sourceObj._loadTile; // -- replace _loadTile with own implementation sourceObj._loadTile = function (id) { // -- we have to patch sourceObj.url, dirty ! // -- we basically change url on the fly with correct BBOX coordinates // -- and leave rest on original _loadTile processing var origUrl =sourceObj.tiles[0] .substring(0,sourceObj.tiles[0].indexOf('&BBOX')); var origUrl = origUrl +"&BBOX={mleft},{mbottom},{mright},{mtop}"; sourceObj.tiles[0] = patchUrl(id, [origUrl]); // -- call original method return origFunc.call(sourceObj, id); }
gist available here
I agree with this blog “Learning how to use Maps in the .Net world is a never ending business. There are major API-differences between WPF, Silverlight, Windows 8 Store apps and Windows Phone and even between WP7 and WP8. Windows Phone 8.1 makes no differ and there are many breaking changes here”
my tests of previous WMS overlays are listed here:
Silverlight : https://blog.sumbera.com/2010/02/25/overlay-wms-on-silverlight-bing/
Windows Phone 7: https://blog.sumbera.com/2011/02/18/windows-phone-7-wms-test/
Windows Phone 8: https://blog.sumbera.com/2013/03/10/tiled-wms-overlay-in-windows-phone-8/
I have put together Visual studio 2013, Update2 Solution that does WMS overlay in Windows 8.1 and Windows Phone 8.1 called WMSonWin81 – here on github
Video of Windows Phone 8.1 below, used “Project My Screen App” to project the app on desktop and record.
WMSonWin81
WMSonWin81using universal app for Windows Store and Windows Phone Store apps Both apps do not share same namespace for map nor component. Windows Store is using Bing Map while Windows Phone is using map control as part of the WP8.1 framework located in Windows.UI.Xaml.Controls.Maps
there are main 2 projects:
WMSOnWin.Windows
Sample code of using WMS source on Windows Store Apps using Bing Maps:
MapTileLayer mapTileLayer = new MapTileLayer(); mapTileLayer.GetTileUri += delegate(object sender, GetTileUriEventArgs e) { Rect mercBounds = GlobalMercator.TileBounds(new Tile(e.X, e.Y), e.LevelOfDetail); e.Uri = new Uri(string.Format(_wmsUrl, mercBounds.Left, Math.Abs(mercBounds.Bottom), mercBounds.Right, Math.Abs(mercBounds.Top))); };
_bingMap.TileLayers.Add(mapTileLayer);
WMSOnWin.WindowsPhone
Sample code of using WMS on Windows Phone Store App using Windows.UI.Xaml.Controls.Maps; core class to look is HttpMapTileDataSource
HttpMapTileDataSource dataSource = new HttpMapTileDataSource();
dataSource.UriRequested +=
new TypedEventHandler<HttpMapTileDataSource, MapTileUriRequestedEventArgs>(
(source, args) => {
Rect mercBounds = GlobalMercator.TileBounds(
new Tile(args.X, args.Y), args.ZoomLevel);
args.Request.Uri = new Uri(string.Format(_wmsUrl, mercBounds.Left,
Math.Abs(mercBounds.Bottom), mercBounds.Right, Math.Abs(mercBounds.Top))); ;
});
_map.TileSources.Add(new MapTileSource(dataSource));
Tested on Windows 8.1 64bit, and Lumia 810, Windows Phone 8.1
enjoy.
Credits: got help here to repair problems with USB connection to my Lumia 810 (had to uninstall USB driver for the phone in device manager) and here to get WP8.1 on Lumia 810
Updated WMS over MapKit sample code for iOS7 , available on github I have added cadastral maps of Czech Republic, used camera API to set the view and tested, check also WMS on Google Maps SDK on iOS mentioned here
iOS7 introduced new class MKTileOverlay sample derives from this class WMSTileOverlay
Key method to custom tile loading (and cache control) is loadTileAtPath:result
- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *tileData, NSError *error)) result
this method is called by MapKit (or better by MKTileOverlayRenderer ) when it needs to draw a tile . It asks for NSData (and error) from x,y,z tile coordinates. In this method you can load NSData either from local cache or from NSURLConnection and pass resulting NSData (when ready) back to MapKit, for example like this (reading from cache)
result ([NSData dataWithContentsOfFile:filePath], nil);
if you do not need to use cache and you do not provide loadTileAtPath method , you can use another hook (callback) that is provided by MKTileOverlay, URLForTilePath:path
- (NSURL *)URLForTilePath:(MKTileOverlayPath)path
this method enables to custom format URL required to load tile, thus you can use WMS HTTP-GET parameters, for example :
NSString * resolvedUrl = [NSString stringWithFormat:@"%@&BBOX=%f,%f,%f,%f",self.url,left,bottom,right,top];
if there is neither method in the derived class, then you probably do not need to derive at all from MKTileOverlay and directly use it with initWithUrlTemplate (not case for WMS, but for any other x,y,z sources)
Bad news is that MapKit on iOS7 doesn’t support tilt/pinch in Satellite/Hybrid mode in MapKit on iOS7
Sample for using WMS sources in Google Maps SDK for iOS. available on github here: https://github.com/Sumbera/WMS_iOS_GoogleMapSDK
Provide your API key in the WMSController.h
There are two ways of overlaying WMS in the Google Maps for iOS SDK:
“Method B”: use GMSTileURLConstructor
// -- method B. WMS tile layer with GMSTileURLConstructor GMSTileURLConstructor urls = ^(NSUInteger x, NSUInteger y, NSUInteger z) { BBox bbox = bboxFromXYZ(x,y,z); NSString *urlKN = [NSString stringWithFormat:@"Your WMS url&BBOX=%f,%f,%f,%f", bbox.left, bbox.bottom, bbox.right, bbox.top]; return [NSURL URLWithString:urlKN]; };
“Method A”: use custom TileLayer derived from GMSTileLayer
-(void)requestTileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)z receiver:(id<GMSTileReceiver>)receiver
[self drawTileAtX:x y:y zoom:z Url:urlStr Receiver:receiver] ;
[data writeToFile: filePath atomically:YES]; [self drawTileAtX:x y: y zoom: z Url:urlStr Receiver:receiver] ;
-(void) drawTileAtX: (NSUInteger) x y:(NSUInteger) y zoom:(NSUInteger)zoom Url:(NSString*) url Receiver: (id<GMSTileReceiver>) receiver { UIImage *image = TileLoad(url,NO); [receiver receiveTileWithX:x y:y zoom:zoom image:image]; }
}
both ways are used in this sample.
Update May 2014: version for Windows Phone 8.1 is here: https://blog.sumbera.com/2014/05/23/wms-overlay-on-windows-8-1-and-windows-phone-8-1/
Looking on Windows Phone 8 Map SDK, I have tried to migrate a simple WMS overlay from Windows Phone 7 (Bing map) – blogged here : https://blog.sumbera.com/2010/11/07/tiled-wms-overlay-in-windows-phone-7/
Changes :
– for migration you have to add before migration System.Core into the project (had to edit manualy proj file and add it there)
-Change from *.Controls.Map to *Map.Controls
– XAML definition
<maps:Map x:Name="sampleMap" LandmarksEnabled="False" Loaded="sampleMap_Loaded" CartographicMode="Hybrid" Center="49.320574,16.68942" ZoomLevel="14" />
Loading TileSource:
private void sampleMap_Loaded(object sender, RoutedEventArgs e) { MapsSettings.ApplicationContext.ApplicationId = "<applicationid>"; MapsSettings.ApplicationContext.AuthenticationToken = "<authenticationtoken>"; TileSource wmsTileSource = new WMSTile(); sampleMap.TileSources.Add(wmsTileSource); }
..then in your TileSource you can use as usual your method GetUri.
Sample code based on Microsoft Sample can be downloaded here: www.sumbera.com/lab/WP8/WMSonWP8.zip
update May 2014: iOS7 version and notes available here: https://blog.sumbera.com/2014/05/17/wms-on-mapkit-with-ios7/
I have crafted really simple and quick code at ESA App dev camp for viewing WMS sources. And as few people questioned me on how to do this, I am posting the full code of the MapView component that takes sample WMS service (Ozone) and overlays this above MapKit.
In github here : https://github.com/Sumbera/WMSoverMapKit
you will find MapViewController that accepts WMS sources stored as array with BBOX %f, %f, %f, %f
-(void) addWMSOverlays: (NSArray*) overlays
this can be called from your rootController:
MapViewController * mapViewController = [[MapViewController alloc] init]; WMSOverlay * wmsOverlay =[[WMSOverlay alloc] initWithName:@"Ozone" Url:@"http://wdc.dlr.de/ogc/produkt_t?LAYERS=GOME2_O3& TRANSPARENT=TRUE& FORMAT=image/png& STYLES=& SLD=http%3A%2F%2Fwdc.dlr.de%2Fsld%2FGOME2_O3_sld.xml& TIME=2012-02-12T00:00:00Z& SERVICE=WMS& VERSION=1.1.1& REQUEST=GetMap& SRS=EPSG:4326& WIDTH=256& HEIGHT=256" Opacity:0.5]; [mapViewController addWMSOverlays:[NSArray arrayWithObjects:wmsOverlay, nil]];
please note:
#1. that sample code uses experimentaly MKNetworkKit, which has some occasional troubles. You can replace the download method in WMSOverlayView class downloadTile
#2 it uses simple hash for storing tiles on cache.
enjoy
Update #1 13/03/2013, here is new blog about Tiled WMS overlay for Windows Phone 8 :https://blog.sumbera.com/2013/03/10/tiled-wms-overlay-in-windows-phone-8/
I have quickly tested my new Windows Phone 7 (Samsung Omnia) device with the Czech cadastral map WMS overlay over the BingMaps and DeepEarth (also see this blog here: https://blog.sumbera.com/2010/11/10/wms-overlay-on-bing-maps-vs-deepearth-on-wp7/) . if you are familiar with www.ikatastr.cz and iPhone version of it (iKatastr) than this example is using same data sources.
In short : it was a great experience – WP7 was smoothly registered, automaticaly connected to internet even without a SIM card using USB cable and Visual Studio 2010 integration just works perfect (so far:) In comparison to many difficulties and ‘certification hell’ on iPhone this is a great relief. Now giving the fact that Nokia is going to support and develop Windows Phone 7, great user experience with WP7 and (for me and menay others) great development experience, this might change the mobile landscape significantly over the 1 or 2 years.
I will guess here that WP7 platform will exceed number of iPhone applications in less than 2 years.
Update #2 Reviewing this after 2 years (March / 2013), – something went wrong with this estimation, but do you remember Gartner predictions from that time ? for example here : http://www.globalnerdy.com/2012/05/07/the-windows-phone-predictions-that-idc-gartner-and-pyramid-research-probably-hope-youve-forgotten/
but we are not yet in 2015, so we will see.
Update #3 02/2016 : this estimation was completely wrong , Windows Phone platform is nowhere, looks like great lesson learned – even you own great language and framework (C# and .NET) there is nothing that guarantees you being successful on emerging/disruptive platform/form factor. Objective-C (then Swift) or Java on Android got this mobile cake.
this is the third sample of the tiled WMS overlay over the Spherical Mercator, this time over the new Google Map v3. Previsous post talked about overlyaing WMS in Silverlight Bing maps (https://blog.sumbera.com/2010/02/25/overlay-wms-on-google-in-silverlight-bing/ ) and OpenLayers (https://blog.sumbera.com/2010/02/17/overlay-wms-on-google-in-openlayers/).
Sample application can be found here: http://www.sumbera.com/lab/GoogleV3/tiledWMSoverlayGoogleV3.htm You can try to run it on your mobile device as well – suprisingly it run very well on my iPhone (sometimes it just crash Safari :), however on the iPad there are some more serious issues that will be hopefuly resolved with new iOS update (JavaScript stops to run).
For the new Google Map v3 you have to do the following :
//Define custom WMS tiled layer var SLPLayer = new google.maps.ImageMapType ( { getTileUrl: function (coord, zoom) { var proj = map.getProjection(); var zfactor = Math.pow(2, zoom); // get Long Lat coordinates var top = proj.fromPointToLatLng( new google.maps.Point(coord.x * 256 / zfactor, coord.y * 256 / zfactor) ); var bot = proj.fromPointToLatLng( new google.maps.Point((coord.x + 1) * 256 / zfactor, (coord.y + 1) * 256 / zfactor)); //corrections for the slight shift of the SLP (mapserver) var deltaX = 0.0013; var deltaY = 0.00058; //create the Bounding box string var bbox = (top.lng() + deltaX) + "," +(bot.lat() + deltaY) +"," +(bot.lng() + deltaX) +"," +(top.lat() + deltaY); //base WMS URL var url = "http://mapserver-slp.mendelu.cz/cgi-bin/mapserv?map=/var/local/slp/krtinyWMS.map&" ; url +="&REQUEST=GetMap"; //WMS operation url +="&SERVICE=WMS"; //WMS service url +="&VERSION=1.1.1"; //WMS version url +="&LAYERS=" + "typologie,hm2003"; //WMS layers url +="&FORMAT=image/png"; //WMS format url +="&BGCOLOR=0xFFFFFF" ; url +="&TRANSPARENT=TRUE" ; url +="&SRS=EPSG:4326"; //set WGS84 url +="&BBOX="+ bbox; // set bounding box url +="&WIDTH=256"; //tile size in google url +="&HEIGHT=256" ; return url; // return URL for the tile }, //getTileURL
//add WMS layer
map.overlayMapTypes.push(SLPLayer);
[Note: therea are related post: overlyaing tiled WMS over the new Google Map v3 https://blog.sumbera.com/2010/11/02/tiled-wms-overlay-on-google-map-v3/ and overlying tiled WMS over the Silverlight Bing map https://blog.sumbera.com/2010/02/25/overlay-wms-on-google-in-silverlight-bing/ ]
Is it possible to display WMS (EPSG:4326) over the Google (EPSG:900913) in Openlayers ? Yes ! thanks to the great img ‘feature’ that enables you to shrink/expand your return image based on defined image size. That means that if your map view is rectangular or you request WMS as tiles (rectangular too) you get proper overlay of EPSG:4326 on EPSG:900913) . Example of various image sizes follows (these are actually WMS GetMap requests):
256 x 160
256 x 256
Here is the way how to implement it in OpenLayers – very simplified:
1. read this post http://docs.openlayers.org/library/spherical_mercator.html and create your Google map:
var options = {
projection: new OpenLayers.Projection(“EPSG:900913”),
displayProjection: new OpenLayers.Projection(“EPSG:4326”),
units: “m”,
numZoomLevels: 22,
maxExtent: new OpenLayers.Bounds(-20037508, -20037508,
20037508, 20037508.34)
};
map = new OpenLayers.Map(‘map’, options);
// create Google Mercator layers
var ghyb = new OpenLayers.Layer.Google(
“Google Hybrid”,
{ type: G_HYBRID_MAP, ‘sphericalMercator’: true }
);
2. add your WMS layer
var gwms = new OpenLayers.Layer.TMS(“SLP”, “http://mapserver-slp.mendelu.cz/cgi-bin/mapserv?map=/var/local/slp/krtinyWMS.map&”,
{
layers: ‘obrys,typologie,hm2003’,
type: ‘png’,
visibility: true,
getURL: get_wms_url,
format: “image/png”,
opacity: 1,
isBaseLayer: false,
deltaX: 0.0013,
deltaY: 0.00058
});
3. include support for reprojection before you include OpenLayers:
<script src =”proj4js/lib/proj4js-combined.js”>script>
4. handle WMS as TMS tiles as this:
function get_wms_url(bounds) {
// recalculate bounds from Google to WGS
var proj = new OpenLayers.Projection(“EPSG:4326”);
bounds.transform(map.getProjectionObject(), proj);
// this is not necessary for most servers display overlay correctly,
//but in my case the WMS has been slightly shifted, so I had to correct this with this delta shift
bounds.left += this.deltaX;
bounds.right += this.deltaX;
bounds.top += this.deltaY;
bounds.bottom += this.deltaY;
//construct WMS request
var url = this.url;
url += “&REQUEST=GetMap”;
url += “&SERVICE=WMS”;
url += “&VERSION=1.1.1”;
url += “&LAYERS=” + this.layers;
url += “&FORMAT=” + this.format;
url += “&TRANSPARENT=TRUE”;
url += “&SRS=” + “EPSG:4326”;
url += “&BBOX=” + bounds.toBBOX();
url += “&WIDTH=” + this.tileSize.w;
url += “&HEIGHT=” + this.tileSize.h;
return url;
}
That is, live example you can see here http://www.sumbera.com/lab/wms/getcapWGS.htm
or in MapShake here : http://www.mapshake.cz/mapfs.aspx?i=464