Joy of Open Source

*** update IV/2017:  from comment below, check this great raster plug-in  from Victor Veralde Gutiérrez too: https://github.com/IHCantabria/Leaflet.CanvasLayer.Field/  ***

I have got great, absolutely amazing  joy today from my own open source experiment. In 2014, nearly 2 years ago,  I have published on GitHub (gist)  L.CanvasOverlay a small class  to handle generic drawing on top of Leaflet, I was thinking let’s try to  contribute back to the open source with this little snippet –  I thought would be useful so I have added little description. Original blog post here: https://blog.sumbera.com/2014/04/20/leaflet-canvas/

After few months later I found it is used on http://windyty.com   one of the best 2015 geo-visualization with huge popularity. It is a small part of this great app, but makes me feel so good like I am part of it, I am looking on something where is my small piece, small share…meaning of the effort, sense of publishing and open sourcing.

Screen Shot 2016-02-02 at 23.16.13

And today I have got echo it is used also in Marine National Facility here: http://www.cmar.csiro.au/data/underway.test/

This makes me so happy … and just came across this quote : “Revenue is a lagging indicator, usage is a leading indicator,” can’t remember, who just said that ? :)

WMS overlay with MapBox-gl-js 0.5.2

alt textQuick 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:

 

  • Needs some ‘hacks’ as current version of the API doesn’t have enough events to supply custom URL before it is loaded. But check latest version of mapbox, it might have better support for this.
  • Another issue is that WMS server has to provide HTTP header with Access-Control-Allow-Origin:* to avoid WebGL CORS failure when loading image (gl.texImage2D). Usually WMS servers don’t care about this, as for normal img tags CORS doesn’t apply. Here WebGL has access to raw image data so WMS provider has to explicitly agree with this.
  • Build process of mapbox-gl-js tend to be as many other large js projects complicated, slow, complex. And specifically on Windows platform it is more difficult to get mapbox-gl-js install and build running then on Mac.

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

WebGL polyline tessellation with MapBox-GL-JS

update 09/20015 : test of tesspathy.js library here . Other sources to look:

  1.  http://mattdesl.svbtle.com/drawing-lines-is-hard
  2.  https://github.com/mattdesl/extrude-polyline
  3. https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader

*** original post ***

This post attempted to use pixi.js tessellation of the polyline, this time let’s look on how mapbox-gl-js can do this. In short much more better than pixi.js.

it took slightly more time to get the right routines from mapbox-gl-js and find-out where the tessellation is calculated and drawn. It is actually on two places – in LinBucket.js  and in line shader. FireFox shader editor helped a lot to simplify and extract needed calculations and bring it into the JavaScript (for simplification, note however that shader based approach is the right one as you can influence dynamically thickness of lines, while having precaluclated mesh means each time you need to change thickness of line you have to recalculate whol e mesh and update buffers )

 

// — module require mockups so we can use orig files unmodified
 module = {};
 reqMap = {
‘./elementgroups.js’: ‘ElementGroups’,
‘./buffer.js’ : ‘Buffer’
};
require = function (jsFile) { return eval(reqMap[jsFile]); };

 

   <!-- all mapbox dependency for tesselation of the polyline -->
 <script src="http://www.sumbera.com/gist/js/mapbox/pointGeometry.js"></script>
    <script src="http://www.sumbera.com/gist/js/mapbox/buffer.js"></script>
    <script src="http://www.sumbera.com/gist/js/mapbox/linevertexbuffer.js"></script>
    <script src="http://www.sumbera.com/gist/js/mapbox/lineelementbuffer.js"></script>
    <script src="http://www.sumbera.com/gist/js/mapbox/elementgroups.js"></script>
    <script src="http://www.sumbera.com/gist/js/mapbox/linebucket.js"></script>
    <script src="http://www.sumbera.com/gist/data/route.js" charset="utf-8"></script>
 // -- we don't use these buffers, override them later, just set them for addLine func
 var bucket = new LineBucket({}, {
 lineVertex: (LineVertexBuffer.prototype.defaultLength = 16, new LineVertexBuffer()),
 lineElement: (LineElementBuffer.prototype.defaultLength = 16, new LineElementBuffer())
 });

var u_linewidth = { x: 0.00015 };
// override .add to get calculated points
LineVertexBuffer.prototype.add = function (point, extrude, tx, ty, linesofar) {
    point.x = point.x + (u_linewidth.x * LineVertexBuffer.extrudeScale * extrude.x * 0.015873);
    point.y = point.y + (u_linewidth.x * LineVertexBuffer.extrudeScale * extrude.y * 0.015873);
    verts.push( point.x, point.y);
    return this.index;
};

// — pass vertexes into the addLine func that will calculate points
bucket.addLine(rawVerts,“miter”,“butt”,2,1);

prototype  code posted here

 

Data Visualisation with d3.js Cookbook

datavisbookbought this great book on  d3.js (Data-Driven documents), written by  Nick Qi Zhu

D3 can plot map in various projections, however do not expect to get same set of (overlapping) functionality as Leaflet or OpenLayers. D3 can extend these mapping frameworks. For OL3 simple  example is here for Leaflet, my favourite is  hexbins or check my own experiment here.

While D3 is kind of ‘base’ charting library (lot of utility functions, helpers) , there is upper , high level library too  that can provide lot of ‘boilerplate’ for common charts. Here comes great news:

Nick Qi Zhu   is also author of the open source javascript  lib dc.js   (Dimensional Charting) library  that is using crossfilter (Fast Multidimensional Filtering for Coordinated Views) . Part of the dc.js lib is set of most common charts and one of them is choropleth map .

Other resources:

D3 Animation : http://blog.visual.ly/creating-animations-and-transitions-with-d3-js/

Design selections: http://www.awwwards.com/fresh-ui-inspiration-in-the-era-of-google-material-and-design-patterns.html

Leaflet 0.7 vs. OpenLayers 3 beta 1 on iOS

leafol2Made quick test of these 2 +1 HTML5 renderers on iOS running inside iOS app in the WebView, that is without Nitro acceleration.  All run on iPad Air

Leaflet 0.7 : great , works fine, everywhere, doesn’t load while dragging map (on mobile only) , runs on Microsoft  Surface too.

OpenLayers 3 beta 1 : runs fine too, loads map during dragging, seems like smaller framerate, can over zoom OSM, doesn’t run in Microsoft Surface well.

Seznam Mapy Api v 4 – proprietary renderer from Seznam , bad rendering  on iOS, missing tiles, nice map sources

Videos and original web pages used: – screencasted by AirPlay – that is directly from iPad Air:

B. OpenLayers 3 beta1
C. Mapy Api v 4.8

OpenLayers or GoogleMap v3 for mobile web app ?

[2014 January] UPDATE : new Openlayers 3 beta 1 works quite well on iPad as well as Laeflet 0.7, check post here: https://blog.sumbera.com/2014/01/23/leaflet-0-7-vs-openlayers-3-beta-1-on-ios/

[2011 September] UPDATE : new Openlayers 2.11 works quite well on iPad/iPhone.

 

I have experimented with OpenLayers 2.9 using touch.js extension for capturing touch events from iPhone/iPad. Although stripped down version of OL is about 184KB, the performance is very bad. If you have iPhone/iPad check this experiment: http://www.sumbera.com/lab/iphone/katastr.htm (note it will not work from desktop browser)

OL team is working on v3 of OL to be more lighter, faster even for mobile devices, see here: http://trac.osgeo.org/openlayers/wiki/three  or here : http://openlayers.org/blog/2010/06/30/openlayers-3-on-github/

Meanwhile I have tested new GoogleMaps v3 how it works on iPhone/iPad. despite few problems (stability, problems with cached tiles or disconnected JavaScirpt  to load new tiles -iPad)  it works pretty well . Check yourself this page from your mobile touch device (iPhone.iPad,Android): http://www.ikatastr.cz/iKatastrM.htm

Conclusion : Google Maps v3 wins on mobile over the Openlayers 2.9

 

iKatastr / MapShake

Poznamka 2018:  tohle je archivni blog o tom, z ceho vznikl iKatastr – byl to vlastne   derivat (vygenerovana mapa) jinak neuspesneho  projektu MapShake.czScreen Shot 2018-10-02 at 6.09.41 PM

iKatastr enables to view cadastral information in Czech Republic and view the owner of the parcel/building.  I would like to mention here few technical details – iKatastr is actually less programmed but more configured. iKatastr on mobile uses Universal Map Format that defines the data sources for map composition and the query request when tapping on-screen. iKatastr in web do the same thing, it uses JSON definition to read full map composition. Moreover web version is linked with MapShake where the configuration of the map can be authored, verified, shared with friends and even you can generate your own iKatastr map as HTML page. the map you see on www.ikatastr.cz is the same that is stored on Mapshake here : http://www.mapshake.cz/549-iKatastr . To generate map similar to www.ikatastr.cz you need to press the <html> button   in the lower left corner to get this page: http://www.mapshake.cz/mapjs.aspx?i=549 from where you can copy/paste the full JavaScript stack to regenerate map on your side.  In the generated map you can also find this linked JavaScript : http://www.mapshake.cz/mi/549.js that is used  by www.ikatastr.cz.

So the bottom line is : do not programme, rather  generate / configure your maps !

Overlay WMS on Google in OpenLayers

[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

OLON in MapShake

It has been a while since I have created WMSBinder.( http://www.sumbera.com/lab/wmsbinder/getcap.htm) Finally I have updated and merged it with MapShake (create new map). So you can enjoy it inside the environment that can store your open layers maps and share them with your friends. MapShake now stores all map compositions as OLON. you can even import your own OLON (but not all formats has been tested).  There are many other small updates as well (new toolbar for map, WMS catalog – only for Czech so far, preview of attached layers, base maps to start from etc..)

To get a preview you can check following maps:

Cloud Made: http://www.mapshake.cz/422-CloudMaded
Catalunia – very fast and cool maps in Spain http://www.mapshake.cz/421-Catalunia
Mapy CZ (Czech based maps) http://www.mapshake.cz/416-MapyCz_a_vektor
Prague cadaster and nocie maps http://www.mapshake.cz/mapfs.aspx?i=408

best thing to try it is to login into MapShake and try to compose your own map – well if you understand Czech language – definitely this would be easier for you;)

OLON for this map follows:

{
    “layers”: [
        {
            “name”: “OverView”,
            “url”: “/MapShake/j/img/blank.gif”,
            “params”: {
                “LAYERS”: “doprava”,
                “SERVICE”: “WMS”,
                “VERSION”: “1.1.1”,
                “REQUEST”: “GetMap”,
                “STYLES”: “”,
                “EXCEPTIONS”: “application/vnd.ogc.se_inimage”,
                “FORMAT”: “image/jpeg”,
                “SRS”: “epsg:102067”
            },
            “singleTile”: true,
            “tileSize”: {
                “w”: 1095,
                “h”: 525,
                “CLASS_NAME”: “OpenLayers.Size”
            },
            “buffer”: 2,
            “reproject”: false,
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -905000,
                “bottom”: -1230000,
                “right”: -400000,
                “top”: -900000,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “displayOutsideMaxExtent”: false,
            “isBaseLayer”: true,
            “displayInLayerSwitcher”: false,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.WMS”
        },
        {
            “name”: “Základní mapa ČR “,
            “url”: “http://apps.esdi-humboldt.cz/data/tilecache/“,
            “layername”: “topoJTSK/2M”,
            “displayOutsideMaxExtent”: false,
            “singleTile”: false,
            “tileSize”: {
                “w”: 256,
                “h”: 256,
                “CLASS_NAME”: “OpenLayers.Size”
            },
            “buffer”: 0,
            “reproject”: false,
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -905000,
                “bottom”: -1230000,
                “right”: -400000,
                “top”: -900000,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “isBaseLayer”: false,
            “displayInLayerSwitcher”: true,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.TileCache”
        },
        {
            “name”: “Základní mapa ČR detail “,
            “url”: “http://apps.esdi-humboldt.cz/cgi-bin/tilecache/tilecache.cgi“,
            “params”: {
                “LAYERS”: “ceniaTopoJTSK”,
                “FORMAT”: “image/png”,
                “SERVICE”: “WMS”,
                “VERSION”: “1.1.1”,
                “REQUEST”: “GetMap”,
                “STYLES”: “”,
                “EXCEPTIONS”: “application/vnd.ogc.se_inimage”,
                “SRS”: “epsg:102067”
            },
            “singleTile”: false,
            “tileSize”: {
                “w”: 256,
                “h”: 256,
                “CLASS_NAME”: “OpenLayers.Size”
            },
            “buffer”: 0,
            “reproject”: false,
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -905000,
                “bottom”: -1230000,
                “right”: -400000,
                “top”: -900000,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “displayOutsideMaxExtent”: false,
            “isBaseLayer”: false,
            “displayInLayerSwitcher”: true,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.WMS”
        },
        {
            “name”: “Letecký snímek ČR “,
            “url”: “http://apps.esdi-humboldt.cz/cgi-bin/tilecache/tilecache.cgi“,
            “params”: {
                “LAYERS”: “ceniaJTSK”,
                “FORMAT”: “image/jpeg”,
                “SERVICE”: “WMS”,
                “VERSION”: “1.1.1”,
                “REQUEST”: “GetMap”,
                “STYLES”: “”,
                “EXCEPTIONS”: “application/vnd.ogc.se_inimage”,
                “SRS”: “epsg:102067”
            },
            “singleTile”: false,
            “tileSize”: {
                “w”: 256,
                “h”: 256,
                “CLASS_NAME”: “OpenLayers.Size”
            },
            “buffer”: 0,
            “reproject”: false,
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -905000,
                “bottom”: -1230000,
                “right”: -400000,
                “top”: -900000,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “displayOutsideMaxExtent”: false,
            “isBaseLayer”: false,
            “displayInLayerSwitcher”: true,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.WMS”
        },
        {
            “name”: “mapy KN – inverzni”,
            “url”: “http://wms.cuzk.cz/wms.asp?”,
            “params”: {
                “LAYERS”: “KN-I”,
                “TRANSPARENT”: true,
                “FORMAT”: “image/png”,
                “SRS”: “epsg:102067”,
                “SERVICE”: “WMS”,
                “VERSION”: “1.1.1”,
                “REQUEST”: “GetMap”,
                “STYLES”: “”,
                “EXCEPTIONS”: “application/vnd.ogc.se_inimage”
            },
            “singleTile”: true,
            “tileSize”: {
                “w”: 1095,
                “h”: 525,
                “CLASS_NAME”: “OpenLayers.Size”
            },
            “buffer”: 2,
            “reproject”: false,
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -764972.5503759343,
                “bottom”: -1054780.0332965641,
                “right”: -713467.0226333638,
                “top”: -1030085.6021871124,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “displayOutsideMaxExtent”: false,
            “isBaseLayer”: false,
            “displayInLayerSwitcher”: true,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.WMS”
        },
        {
            “name”: “Praha – hladiny hlukoveho ukazatele Ldvn (dB)”,
            “url”: “http://castor.cenia.cz:80/wmsconnector/com.esri.wms.Esrimap/cenia_hluk_pbol?”,
            “params”: {
                “LAYERS”: “40”,
                “TRANSPARENT”: true,
                “FORMAT”: “image/png”,
                “SRS”: “epsg:102067”,
                “SERVICE”: “WMS”,
                “VERSION”: “1.1.1”,
                “REQUEST”: “GetMap”,
                “STYLES”: “”,
                “EXCEPTIONS”: “application/vnd.ogc.se_inimage”
            },
            “singleTile”: true,
            “tileSize”: {
                “w”: 1095,
                “h”: 525,
                “CLASS_NAME”: “OpenLayers.Size”
            },
            “buffer”: 2,
            “reproject”: false,
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -742465.3403076057,
                “bottom”: -1044817.9470094242,
                “right”: -741950.2850301799,
                “top”: -1044571.0026983297,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “displayOutsideMaxExtent”: false,
            “isBaseLayer”: false,
            “displayInLayerSwitcher”: true,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.WMS”
        },
        {
            “name”: “Editable Vectors”,
            “features”: [
            ],
            “numZoomLevels”: 14,
            “maxResolution”: 705.5551745557614,
            “minResolution”: 0.035277758727788065,
            “maxExtent”: {
                “left”: -905000,
                “bottom”: -1230000,
                “right”: -400000,
                “top”: -900000,
                “CLASS_NAME”: “OpenLayers.Bounds”
            },
            “wrapDateLine”: false,
            “displayOutsideMaxExtent”: false,
            “isBaseLayer”: false,
            “displayInLayerSwitcher”: true,
            “visibility”: true,
            “opacity”: null,
            “alwaysInRange”: true,
            “CLASS_NAME”: “OpenLayers.Layer.Vector”
        }
    ],
    “center”: {
        “lon”: -742207.8126688928,
        “lat”: -1044694.474853877,
        “CLASS_NAME”: “OpenLayers.LonLat”
    },
    “resolution”: 0.7055551745557613,
    “resolutions”: [
        705.5551745557614,
        352.7775872778807,
        176.38879363894034,
        70.55551745557612,
        35.27775872778806,
        17.63887936389403,
        7.055551745557612,
        3.527775872778806,
        1.763887936389403,
        0.7055551745557613,
        0.35277758727788,
        0.17638879363,
        0.07055551745557613,
        0.035277758727788065
    ],
    “bounds”: {
        “left”: -742465.3403076057,
        “bottom”: -1044817.9470094242,
        “right”: -741950.2850301799,
        “top”: -1044571.0026983297,
        “CLASS_NAME”: “OpenLayers.Bounds”
    },
    “projection”: “epsg:102067”,
    “units”: “m”,
    “maxResolution”: “auto”,
    “numZoomLevels”: 20,
    “zoom”: 9,
    “CLASS_NAME”: “OpenLayers.Map”
}