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

OGC helper #2362

Closed
wants to merge 20 commits into from
Closed

OGC helper #2362

wants to merge 20 commits into from

Conversation

kaktus40
Copy link
Contributor

Hello,
like I said in my mail with the CLA, I make a contributition that can parse the capabilities of Web Map Service, Tile Map Service and Web Map Tile Service.
I can't find a public server that don't have a CORS restriction also it's difficult to make a sandCastle example.
Nevertheless, if you have an accessible WMS, TMS or WMTS, you can use this code:
var tabCapabilities;
Cesium.OGCHelper.parser({service:'XXX',url:'URL'}).then(function(tab){console.log(tab);tabCapabilities=tab});
console.log(tabCapabilities);

where

It's my first pull and I fear that it's a poor contribution also I'm waiting your remarks

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 31, 2014

Thanks @kaktus40! @kring can you review this?

- SortRequired libraries (ant sortRequire)
- modify OGCCapabilities and OGCHelper in order to be compliant with
JSHint
- add coverage tests for OGCCapabilities and OGCHelper
@kaktus40
Copy link
Contributor Author

kaktus40 commented Jan 2, 2015

I made a sandCastle example but while I tested it, I had an issue with tiles using WebMercatorTilingScheme. I have no issue with tile using GeographicTilingScheme. The error stack is in attachment.
error

The sandCastle code for my contribution is :
//url of a Tile Map resource server, of a Web Map resource server or of Web Map Tile resource server.
var url='http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/1.0.0/WMTSCapabilities.xml';
// could be WMS, WMTS or TMS according to the type of server defined by url.
var service='WMTS';

var myImageryProvider=function(capabilities,options){
        options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);
        this._capabilities=capabilities;
        this._tileDiscardPolicy = options.tileDiscardPolicy;

        this._errorEvent = new Cesium.Event();

        var credit = options.credit;
        this._credit = typeof credit === 'string' ? new Cesium.Credit(credit) : credit;
}

Cesium.defineProperties(myImageryProvider.prototype, {
        capabilities : {
            get : function() {
                return this._capabilities;
            }
        },
        tileWidth : {
            get : function() {
                return this._capabilities.tileMapWidth;
            }
        },
        tileHeight : {
            get : function() {
                return this._capabilities.tileMapHeight;
            }
        },
        maximumLevel : {
            get : function() {
                return this._capabilities.maxLevel;
            }
        },
        minimumLevel : {
            get : function() {
                return 0;
            }
        },
        tilingScheme : {
            get : function() {
                return this._capabilities.tilingScheme;
            }
        },
        rectangle : {
            get : function() {
                return this._capabilities.rectangle;
            }
        },
        tileDiscardPolicy : {
            get : function() {
                return this._tileDiscardPolicy;
            }
        },
        errorEvent : {
            get : function() {
                return this._errorEvent;
            }
        },
        ready : {
            value: true
        },
        credit : {
            get : function() {
                return this._credit;
            }
        },
        hasAlphaChannel : {
            get : function() {
                return true;
            }
        }
    });

myImageryProvider.prototype.getTileCredits = function(x, y, level) {
        return undefined;
    };
myImageryProvider.prototype.requestImage = function(x, y, level) {
        var url='';
        var result;
        if(Cesium.defined(this._capabilities.getURLImage)){
            url=this._capabilities.getURLImage(x,y,level);
        }else{
            if(Cesium.defined(this._capabilities.getURLArray)){
                url=this._capabilities.getURLArray(x,y,level);
            }
        }
        if(url!==''){
            result=Cesium.ImageryProvider.loadImage(this,url);
        }
        return result;
    };

var cartographicScratch = new Cesium.Cartographic();
var cartesian3Scratch = new Cesium.Cartesian3();
var rad2deg=180/Math.PI;

myImageryProvider.prototype.pickFeatures = function(x,y,level,longitude,latitude) {
    var rectangle=this.rectangle;
    var result;
    var projected;
        /*if (this.capabilities.tilingScheme instanceof Cesium.GeographicTilingScheme) {
            projected = cartesian3Scratch;
            projected.x = longitude*rad2deg;
            projected.y = latitude*rad2deg;
        } else {
            var cartographic = cartographicScratch;
            cartographic.longitude = longitude;
            cartographic.latitude = latitude;

            projected = this._tilingScheme.projection.project(cartographic, cartesian3Scratch);
        }

        var i = (this.tileWidth * (projected.x - rectangle.west) / rectangle.width) | 0;
        var j = (this.tileHeight * (rectangle.north - projected.y) / rectangle.height) | 0;
        var url;
        if(Cesium.defined(this._capabilities.getURLFeatureInfoImage)){
            url=this._capabilities.getURLFeatureInfoImage(x,y,level,{i:i,j:j});

        }else{
            if(Cesium.defined(this._capabilities.getURLFeatureInfoArray)){
                url=this._capabilities.getURLFeatureInfoArray(x,y,level,{i:i,j:j});
            }
        }
        if(url!==''){
                result=this.capabilities.formatFeatureInfo.loader(url).then(function(info){return this.capabilities.formatFeatureInfo.postProcess(info);});
            }*/
        return result;
    };


Cesium.OGCHelper.parser({service:,url:url}).then(function(tab){
    console.log(tab[0].capabilities);
    try{
    var imageryProvider=new myImageryProvider(tab[0].capabilities);
    var viewer = new Cesium.Viewer('cesiumContainer',{imageryProvider:imageryProvider,baseLayerPicker:false});
        }catch(e){console.log(e);}
});

@mramato
Copy link
Contributor

mramato commented Jan 4, 2015

@kaktus40, with all of the holiday festivities I haven't have a chance to look at this yet; but I just wanted to say thanks!

@kaktus40
Copy link
Contributor Author

kaktus40 commented Jan 8, 2015

Hello, thanks for the congratulations. Maybe I should explain my code and detail some use cases of my contribution. I don't see any change on this pull and maybe it's difficult to apprehend?

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 8, 2015

More explanation is welcomed. I suspect no one has reviewed this yet because everyone is quite busy and the review will be a good amount of work. 😄

@kaktus40
Copy link
Contributor Author

Purposes:

Parsing server resources:

All the resources available from a server can be analysed. OGCHelper can parse the datas and provide exploitable functions to retrieve useful things from classical parameters for tiles (x, y, level).

Following OGC standards:

I made OGCHelper the most compliant to OGC standards and it can parse resource files from:

  • Web Map Service version (WMS) 1.1.1 & 1.3.0
  • Web Map Tile Service (WMTS) 1.0.0
  • Tile Map Service (TMS) 1.0.0 even if it's not an OGC standard

Proposing available services via an unique interface: OGCCapabilities

  • define list of styles and dimensions that can be use. The usual dimensions are elevation and time but it can be wave length of sensor...;
  • define attributes limiting level and space available for a layer;
  • above all define functions to generate URL from tile parameters in order to retrieve the wished tile datas (image or array datas).

Being a low level component

OGCHelper can be used for an ImageryProvider (see code in previous comment where I adjust only requestImage function with an OGCCapabilities object) or a TerrainProvider (see my plugin GeoserverTerrainProvider)

Designing to be generic and easily extended:

Today, Cesium manages two different tilingScheme with their respective projection and ellipsoid (GeographicTilingScheme and WebMercatorTilingScheme), but maybe tomorrow there will be a new tilingScheme, a new projection and a new ellipsoid to use. In the other hand, server can propose their data following lots of datum and not only Coordinate Referential System 84 (or EPSG 4326). To make it easy to add a new tilingScheme, I regroup them in one array (see OGCHelper.CRS). The parameters of each element are used to parse the capabilities of a layer. The same reasoning was used for :

  • image format (for example tiff can not be displayed in firefox unlike in chrome. Maybe one day tiff could be displayed by all internet navigator?),

  • array format,

  • feature info format.

    A new OGC standard can be easily added (one standard= one parsing function).

How to

Explanations

For a basic use, OGCHelper just needs 2 options:

  • service which is a string defining the type of service to use. It should be 'WMS', 'WMTS' or 'TMS'. For example var service='WMTS';
  • one of the two following options:
    • xml can be a string or a xmlDocument that announces the capabilities of a server. This could be helpful when a resource can not send its resource file due to CORS consideration.
    • url is a string describing the url of a server. For example var url='http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/1.0.0/WMTSCapabilities.xml'

OGCHelper.parser function returns a promise of array. Each element of the returned array has two attributes : layerName (name of the layer) and capabilities (object that follows OGCCapabilities contract interface). The most important attribute is ready. If it's true, it means that the capabilities object have a function that converts a tile identifier (x, y and level parameters) into an URL. This URL could be requested to have the data of the identified tile. Depending on the resources, the function(s) to convert a tile identifier into an URL can be:

  • getURLImage if an image format (available in OGCHelper.FormatImage array) had be found in the resources. Optionally, a new format image can be added via formatImage parameter of OGCHelper. The formatImage attribute of OGCCapabilities indicates the selected image format that will be requested with getURLImage
  • getURLArray if an array format (available in OGCHelper.FormatArray array) had be found in the resources. Optionally, a new format image can be added via formatArray parameter of OGCHelper. The *formatArray * attribute of OGCCapabilities indicates the selected array format that will be requested with getURLArray

formatFeatureInfo indicates eventually the format of feature info that could be used to have the most precise datas for a position. If this attribute is available, in combination with:

  • formatImage attribute, the OGCCapabilities will have getURLFeatureInfoImage that will retrieve a feature info URL from a tile identifiers.
  • formatArray attribute, the OGCCapabilities will have getURLFeatureInfoArray that will retrieve a feature info URL from a tile identifiers.

The most important function of OGCCapabilities is getTileDataAvailable that determines if datas are available for a tile. The availability of a tile is defining only from parsing a resource file.

When dimensions and styles attributes are available (length of array greater than 0). Each URL conversion function will take in consideration a default behaviour. The default style that will be use in this case is the first of styles array. For each dimension available for the layer, a default parameter is defined. The default behaviour can be change via options when getURLXXX functions are called.

Example

var service='WMTS';
var url='http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/1.0.0/WMTSCapabilities.xml';
var formatImage={format : 'image/tiff',extension: 'tif'}; //defining an optional format image 

var promise=Cesium.OGCHelper.parser({service:,url:url,formatImage:formatImage});
promise.then(function(capabilitiesTab){
    //capabilitiesTab have a length of 1
    capabilitiesTab.forEach(function(element){
        console.log(element.layerName);//name of the layer
        var capabilities=element.capabilities;
        console.log(capabilities.rectangle);//the rectangle limiting the capabilities
        console.log(capabilities.maxLevel);//the maximum level available for these capabilities
        console.log(capabilities.getURLImage(0,1,0));// should return "http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/tile/1.0.0/WorldTimeZones/default/default028mm/0/1/0.png"
        // there is no getURLArray, formatImage or formatArray functions.

        //if the resource had a dimension named 'elevation', an other named 'time', a style named 'blue' and a formatFeatureInfo available then:
        /*
        console.log(capabilities.getURLImage(0,1,0,{style:"blue",dimensions:[{name:"elevation",value:"300"}]}));// should return something like "http://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer/WMTS/tile/1.0.0/WorldTimeZones/blue/2011-10-04/300/default028mm/0/1/0.png"
        console.log(capabilities.getURLFeatureInfoArray(0,1,0,{style:"blue",dimensions:[{name:"elevation",value:"300"}],i:30,j:15}));/should return something like "http://sampleserver6.arcgisonline.com/WMTS?service=WMTS&request=GetFeatureInfo&version=1.0.0&layer=WorldTimeZones&style=blue&format=image/blunt&TileMatrixSet=default028mm&TileMatrix=0&TileRow=1&TileCol=0&time=2011-10-04&elevation=300&i=30&j=15&infoFormat=info/blunt"
        */
    });
});

Why to use OGCHelper

  • OGCHelper should be used when Cesium needs to retrieve some datas from an OGC service. My first reason to create OGCHelper was a limitation of TileMapServiceImageryProvider: if the server doesn't provide a tilemapresource.xml, the imageryProvider can't work. This name is not a mandatory in the standard!
  • Moreover, when I saw the implementation of WMTS, I found it incomplete and my vision is comforted by some forum questions and regular pulls about WebMapTileServiceImageryProvider. The list of parameters for this imageryProvider begin to become very big (my vision).
  • OGCHelper take in consideration the dimensions and styles.

Sorry for this chapter which seems more an advertising...

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 10, 2015

Thanks @kaktus40. Will this replace the WMS GetCapabilities work we did a while back but never finished?

Sounds like this will be a great contribution to Cesium. I suspect we'll need to at least work on the code organization and naming. @kring are you up for reviewing this?

@kring
Copy link
Member

kring commented Jan 10, 2015

Yes, but it will be a little bit before I get to it, sorry.

@kaktus40
Copy link
Contributor Author

Hello for information, I could retrieve 89 OGCCapabilities objects from nationalmap.nicta.com.au :

var retour;Cesium.OGCHelper.parser({service:'WMS',url:'http://nationalmap.nicta.com.au/proxy/http://geoserver-nm.nicta.com.au/geotopo_250k/ows'}).then(function(tab){console.log(tab.length);retour=tab;})

@kaktus40
Copy link
Contributor Author

kaktus40 commented Feb 5, 2015

Hello,
@kring can you indicate me a slot time when you think you will analyze my pull in order to be available to answer your questions?

@kaktus40
Copy link
Contributor Author

kaktus40 commented May 8, 2015

Hello, I'd like to work on WFS / GML in the same process I used for OGC Helper but I don't have orientations on the code (do I follow the spirit of Cesium??) and I can't capitalize on your critics. Maybe should I create a new class for ISO 8601 validation?

@kring
Copy link
Member

kring commented May 18, 2015

Hi @kaktus40. First, sorry for the long delay in reviewing this. There are two reasons I've been procrastinating merging this, or even giving it a full review:

  • There's a lot of code here, and it doesn't follow the Cesium coding conventions very well. So there's a good amount of grunt work necessary just to bring it in line.
  • More importantly: I'm not really convinced this belongs in Cesium at all.

Let me elaborate on the second point. Having some code to query OGC metadata services and provide useful information is, of course, really valuable for a lot of applications. But that doesn't mean it needs to be part of Cesium. Why not make this a separate library? Cesium could then have a simple ImageryProvider that is constructed with either:

  1. A template URL. Users get the template URL from the OGCHelper library and pass it to Cesium. Or,
  2. Two functions, one that builds a URL for a given tile ID, and another that retrieves feature information given a tile ID and a position. The functions are, once again, obtained from the OGCHelper library and passed to Cesium.

Is there a strong reason to tightly couple this functionality with Cesium?

@kring
Copy link
Member

kring commented May 18, 2015

You mentioned above that one motivation for writing this was the incomplete or incorrect support for querying metadata in TileMapServiceImageryProvider and WebMapTileServiceImageryProvider. I'm definitely sympathetic to that, but I'd argue that both of these providers would be improved if they didn't query metadata at all, but instead expected all the necessary information to be supplied in the constructor. Then, if desired, a user could use a separate library like OGCHelper to determine the necessary parameters from the metadata.

Leaflet, for example, does not include any WMS metadata querying capabilities, yet its TileLayer.WMS makes it easy to hook up to any WMS layer:
https://github.com/Leaflet/Leaflet/blob/master/src/layer/tile/TileLayer.WMS.js

It doesn't have built-in WMTS support, but the WMTS plugin takes the same approach:
https://github.com/mylen/leaflet.TileLayer.WMTS/blob/master/leaflet-tilelayer-wmts-src.js

@kaktus40
Copy link
Contributor Author

Hello,
thanks for your answers. For some future contributions and to correct the present code, I'd like to know my mistake about coding conventions. I thought that my code was compliant with this.

To follow your path, I should make a library or a plug in. Why not, there is no strong reason to be in Cesium, I thought this is a plus like in openLayer (see here and here). After all, I made OGCHelper to be extensible (see the arrays beetwen Line 50 and Line 171) and relatively independent of Cesium (save Ellipsoid and tilingScheme definitions).

I don't work with ArcGisServer but from your code it seems the return of feature information request is a specific xml. Also OGCHelper can provide a way to request but can't give a way to compute the return. Is there a future evolution/contribution to give a way to compute any return of feature information in Cesium?

From Cesium forums, it seems Cesium will implements WFS vectors layer. Does it means you will provide a way to compute GML? Maybe in a datasource?

So to sum up the remaining work:

  • I convert this contribution into a plug in for Cesium.
  • I create a new TileProvider in the plugin to work with Cesium
  • The new TileProvider has an OGCCapabilities (results of parsing metadata from OGC Server) for parameter. From this parameter, it cans retrieve tiles and feature info.

@kaktus40
Copy link
Contributor Author

I have a question: dimension (like time or wavelength see here...) will be a functionality for tile Provider?

@hpinkos
Copy link
Contributor

hpinkos commented Feb 19, 2016

@kring what's the status of this? Do you think this something we might want to add or can this be closed?

@pjcozzi
Copy link
Contributor

pjcozzi commented Jun 3, 2016

Closing given that this hasn't been updated in a while. Thanks all for the support, and please re-open separate PRs for things appropriate for core Cesium.

@pjcozzi pjcozzi closed this Jun 3, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants