Source: api/SpwCSWCatalog.js

Retour à la documentation
define([
    'dojo/_base/declare',
    'dojo/Stateful',
    'dojo/_base/lang',
    'dojo/_base/array',
    'dojo/on',
    'dojo/request',
    'dojo/io-query',
    'dojo/Deferred',

    'spw/api/SpwViewer',
    'spw/api/ConfigLoader',

    'spw/libs/Jsonix-all',

    'spw/libs/ogc/OWS_1_0_0',
    'spw/libs/ogc/DC_1_1_metawal',
    'spw/libs/ogc/DCT',
    'spw/libs/ogc/XLink_1_0',
    'spw/libs/ogc/CSW_2_0_2_metawal',
    'spw/libs/ogc/Filter_1_1_0',
    'spw/libs/ogc/GML_3_1_1',
    'spw/libs/ogc/SMIL_2_0_Language',
    'spw/libs/ogc/SMIL_2_0',
    'spw/libs/ogc/GML_3_2_0',
    'spw/libs/ogc/ISO19139_GCO_20060504',
    'spw/libs/ogc/ISO19139_GMD_20060504',
    'spw/libs/ogc/ISO19139_GTS_20060504',
    'spw/libs/ogc/ISO19139_GSS_20060504',
    'spw/libs/ogc/ISO19139_GSR_20060504',
    'spw/libs/ogc/ISO19139_GMX_20060504',
    'spw/libs/ogc/ISO19139_SRV_20060504'
],
function(declare, Stateful, lang, array, on, request, ioQuery, Deferred, SpwViewer, ConfigLoader, Jsonix,
        OWS_1_0_0, DC_1_1, DCT, XLink_1_0, CSW_2_0_2, Filter_1_1_0, GML_3_1_1,
        SMIL_2_0_Language, SMIL_2_0, GML_3_2_0, ISO19139_GCO_20060504, ISO19139_GMD_20060504,
        ISO19139_GTS_20060504, ISO19139_GSS_20060504, ISO19139_GSR_20060504, ISO19139_GMX_20060504,
        ISO19139_SRV_20060504) {

    /**
     * @class spw.api.SpwCSWCatalog
     * @classdesc Classe permettant d'interroger un service CSW (catalogue de services) version 2.0.2
     */
    return declare('spw.api.SpwCSWCatalog', [Stateful], /** @lends spw.api.SpwCSWCatalog.prototype */ {

        /**
         * Pour communiquer avec le service csw
         */
        proxyPage: SpwViewer.getInstance().get('proxyPageUrl'),


        /**
         * Utiliser CORS si le serveur le permet à la place de la page proxy
         */
        useCors: true,
        

        /**
         * L'url du service
         */
        url: null,


        /**
         * Le context jsonix pour xml <-> json
         */
        jsonixContext: null,


        /**
         * Mapping des propriétés CSW <-> propriétés json d'un record (pour avoir tout le temps la même interface pour un record)
         */
        propertiesMapping: null,


        /**
         * La propriété correspondant à un map service
         */
        serviceProperty: null,


        /**
         * Les protocoles (esri-rest, wms...) possibles avec les informations permettant de le récupérer dans le record
         */
        protocols: null,


        /**
         * Filtre par défaut à appliquer aux requêtes
         */
        defaultFilter: null,


        /**
         * Sort par défaut
         */
        defaultSort: null,

        catalogGroup: 'CSWCATALOG',

        /**
         * @constructs
         */
        constructor: function(opts) {
            lang.mixin(this, opts);
            this.initContext();
        },

        /**
         * Récupère les informations d'un filtre pour un certain champ
         * @param  {String} field le champ (i.e. 'type', 'title'...)
         */
        getFilterInfo: function(field) {
            // on parcourt chaque mapping pour retrouver la propriété correspondante (mapping inverse en somme)
            for (var key in this.propertiesMapping) {
                var item = this.propertiesMapping[key];

                if (item.property === field) {
                    return lang.mixin({
                        field: key
                    }, item);
                }
            }

            return null;
        },

        /**
         * Transforme un filtre en JS classique en JS pour la transformation XML
         * @param  {Array} filter du type [ { field: 'dc:type', like: '%service%' } ]
         */
        transformFilter: function(filter) {
            var toReturn = null;

            // parcourt les filtres et les construit
            array.forEach(filter, lang.hitch(this, function(f) {
                var filterBuilder = new this.FilterBuilder().PropertyName(f.field);

                if (f.like) {
                    filterBuilder.isLike('%' + f.like + '%');
                }
                else if (f.eq) {
                    filterBuilder.isEqualTo(f.eq);
                }
                else if (f.neq) {
                    filterBuilder.isNotEqualTo(f.eq);
                }
                else if (f.gt) {
                    filterBuilder.isGreaterThan(f.eq);
                }
                else if (f.lt) {
                    filterBuilder.isLessThan(f.eq);
                }
                else if (f.gteq) {
                    filterBuilder.isGreaterThanOrEqualTo(f.eq);
                }
                else if (f.lteq) {
                    filterBuilder.isLessThanOrEqualTo(f.eq);
                }
                else if (f['in']) {
                    filterBuilder.isBetween(f['in'][0], f['in'][1]);
                }

                if (toReturn == null) {
                    toReturn = filterBuilder;
                }
                else {
                    // concaténation des filtres (and par défaut)
                    if (f.or) {
                        toReturn.or(filterBuilder);
                    }
                    else {
                        toReturn.and(filterBuilder);
                    }
                }
            }));

            return toReturn;
        },

        /**
         * Transforme un sort en JS classique en JS pour la transformation XML
         * @param  {Array} sort du type [ { field: 'dc:title', desc: true } ]
         */
        transformSort: function(sort) {
            var sortBuilder = new this.SortBuilder();

            array.forEach(sort, lang.hitch(this, function(s) {
                sortBuilder.Sort(s.field, s.desc);
            }));

            return sortBuilder;
        },

        /**
         * GetCapabilities du catalogue (pour récupérer les opérations possibles)
         * TODO: on ne l'exploite pas encore...
         */
        GetCapabilities: function() {
            var deferred = new Deferred();

            // création du json pour la transformation XML
            var getCapabilities = this._GetCapabilities();
            // json -> xml
            var xml = this.jsonToXml(getCapabilities);

            request.post(this.get('url'), {
                data: this.xmlToString(xml),
                headers: {
                    'Content-Type': 'application/xml'
                },
                handleAs: 'xml'
            }).then(lang.hitch(this, function(resp) {
                deferred.resolve(this.xmlToJson(resp));
            }), lang.hitch(this, function(err) {
                deferred.reject(err);
            }));

            return deferred;
        },

        /**
         * Opération GetRecords (récupération des entrées)
         * @param {Integer} start        id du premier record à retourner
         * @param {Integer} max          nombre max de records à retourner
         * @param {Object} opts         filter et sort
         * @param {String} outputSchema schéma xml du record en sortie
         */
        GetRecords: function(start, max, opts, outputSchema) {
            var deferred = new Deferred();

            opts = opts || {};

            var filter = null;
            var sort = null;

            // on construit le filtrage (JS classique en JS csw)
            if (opts.filter) {
                filter = this.transformFilter(opts.filter);

                if (this.defaultFilter) {
                    filter.and(this.transformFilter(this.defaultFilter));
                }
            }
            else if (this.defaultFilter) {
                filter = this.transformFilter(this.defaultFilter);
            }

            // on construit le sort de la même manière
            if (opts.sort) {
                if (this.defaultSort) {
                    opts.sort = opts.sort.concat(this.defaultSort);
                }

                sort = this.transformSort(opts.sort);
            }
            else if (this.defaultSort) {
                sort = this.transformSort(this.defaultSort);
            }

            // on construit la query de la requête
            var query = this._Query('full', filter ? this._Constraint(filter) : null, sort);

            // et on récupère le JS CSW de la requête complète
            var getRecords = this._GetRecords(start, max, query, outputSchema);
            // qu'on transforme en xml
            var xml = this.jsonToXml(getRecords);

            request.post(this.get('url'), {
                data: this.xmlToString(xml),
                headers: {
                    'Content-Type': 'application/xml'
                },
                handleAs: 'xml'
            }).then(lang.hitch(this, function(resp) {

                var json = this.xmlToJson(resp);

                // TODO: gérer les erreurs

                // on récupère les réponses
                var res = json['csw:GetRecordsResponse'];

                if (res == null) {
                    return deferred.reject('no data');
                }

                // résultats de la recherche
                var searchResults = res.searchResults;

                if (searchResults == null) {
                    return deferred.reject('no data');
                }

                // les records
                var records = searchResults.abstractRecord;

                // on récupère les infos
                var tmp = {
                    total: searchResults.numberOfRecordsMatched,
                    length: searchResults.numberOfRecordsReturned,
                    next: searchResults.nextRecord,
                    // on mappe les records CSW pour qu'ils aient la même interface en JS
                    records: array.map(records, lang.hitch(this, function(r, idx) {
                        var newRecord = {};

                        var elements = r['csw:Record'].dcElement;

                        array.forEach(elements, lang.hitch(this, function(e) {
                            this._mapRecordElement(e, newRecord);
                        }));

                        return newRecord;
                    }))
                };

                deferred.resolve(tmp);

            }), lang.hitch(this, function(err) {
                deferred.reject(err);
            }));

            return deferred;
        },

        /**
         * Est-ce que le record est un map service ?
         */
        isMapService: function(element) {
            var mapped = this._getMappedProperty(this.serviceProperty);

            if (mapped == null) {
                return false;
            }

            // on récupère les liens pouvant correspondre à des services
            var links = element[mapped.property];

            if (links == null || (mapped.type === 'array' && links.length === 0)) {
                return false;
            }

            var testLink = function(l) {
                if (l.protocol == null) {
                    return false;
                }

                // on parcourt tous le protocoles et on test si le lien en est un
                return array.some(this.protocols, lang.hitch(this, function(p) {
                    if (l.protocol.indexOf(p.protocol) >= 0) {
                        return true;
                    }

                    return false;
                }));
            };

            // on parcourt les liens et on teste s'il s'agit d'un service
            if (links.length) {
                return array.some(links, lang.hitch(this, testLink));
            }
            else {
                return testLink(links);
            }
        },

        /**
         * Mappe un record CSW en record avec une interface standard
         */
        _mapRecordElement: function(element, record) {
            // on parcourt toutes les propriétés mappées
            for (var key in this.propertiesMapping) {
                if (element[key] == null) {
                    continue;
                }

                // s'il s'agit d'une propriété mappable
                // on récupère ses infos de mapping
                var p = this.propertiesMapping[key];

                // si l'élément ne contient pas de données, on l'ignore
                if (element[key].content == null || element[key].content.length === 0) {
                    continue;
                }

                // s'il s'agit de la propriété pouvant correspondre à un map service
                if (key === this.serviceProperty) {
                    this._mapServiceProperty(element, record, key, p);
                    continue;
                }

                // sinon, on mappe
                if (p.type === 'value') {
                    record[p.property] = element[key].content[0];
                }
                else {
                    record[p.property] = record[p.property] || [];
                    record[p.property].push(element[key].content[0]);
                }
            }
        },

        /**
         * Mappe la propriété pouvant correspondre à un map service
         */
        _mapServiceProperty: function(element, record, key, p) {
            // on mappe statiquement pour ce type de propriété
            if (p.type === 'value') {
                record[p.property] = {
                    link: element[key].content[0],
                    description: p.descriptionProperty ? element[key][p.descriptionProperty] : null,
                    name : p.nameProperty ? element[key][p.nameProperty] : null,
                    protocol: p.protocolProperty ? element[key][p.protocolProperty] : null
                };
            }
            else {
                record[p.property] = record[p.property] || [];

                record[p.property].push({
                    link: element[key].content[0],
                    description: p.descriptionProperty ? element[key][p.descriptionProperty] : null,
                    name : p.nameProperty ? element[key][p.nameProperty] : null,
                    protocol: p.protocolProperty ? element[key][p.protocolProperty] : null
                });
            }
        },

        _getMappedProperty: function(property) {
            return this.propertiesMapping[property];
        },

        /**
         * Ajoute le service d'un record
         */
        addServiceFromURI: function(element) {
            var mapped = this._getMappedProperty(this.serviceProperty);

            if (mapped == null) {
                console.error('no property ' + this.serviceProperty);
                return;
            }

            // on récupère les liens pouvant correspondre à un service
            var links = element[mapped.property];

            if (mapped.type === 'array') {
                // on ajoute suivant une priorité (pour ajouter que le esri rest si on veut)
                var sorted = [];

                array.forEach(links, lang.hitch(this, function(l) {
                    // on récupère la config du service
                    var cfg = this._getMapServiceConfig(element, l);

                    if (cfg) {
                        sorted.push(cfg);
                    }
                }));

                if (sorted.length === 0) {
                    return;
                }

                // on sort le tableau suivant la priorité
                sorted = sorted.sort(function(s1, s2) {
                    return s2.priority - s1.priority;
                });

                var oldPriority = sorted[0].priority;

                // et on ajoute les services les plus prioritaires (ayant la même priorité)
                array.some(sorted, function(s) {
                    if (oldPriority != s.priority) {
                        return true;
                    }

                    oldPriority = s.priority;

                    /* l'ajout de basemap à chaud n'étant pas supporté, l'utilité est très limité
                    if (s.service.isBaseMap) {
                        SpwViewer.getInstance().get('spwMap').addBaseMap(s.service);
                    }
                    else {*/
                    SpwViewer.getInstance().get('spwMap').addMapService(s.service);
                    //}

                    return false;
                });
            }
            else {
                // si un seul lien, c'est plus facile
                var cfg = this._getMapServiceConfig(element, links);

                if (cfg) {
                    SpwViewer.getInstance().get('spwMap').addMapService(cfg.service);
                }
            }
        },

        /**
         * Récupère la config depuis le catalogue pour un certain id
         */
        _getCatalogServiceConfig: function(id) {
            var catalog = ConfigLoader.getInstance().get('catalog');
            var toReturn = null;

            array.some(catalog, lang.hitch(this, function(serviceGroup) {
                var foundGroup = (serviceGroup.code === this.catalogGroup);

                if (!foundGroup) {
                    return false;
                }

                var services = SpwViewer.getInstance().get('spwMap').getServicesFromGroup(serviceGroup);

                array.some(services, lang.hitch(this,function(service){
                    if (service.serviceId === id) {
                        toReturn = service;
                        return true;
                    }
                    return false;
                }));

                return foundGroup;
            }));

            if (toReturn) {
                delete toReturn.serviceId; // pour pouvoir ajouter plusieurs services, il ne faut pas que l'id soit repris
            }

            return toReturn;
        },

        /**
         * Permet de récupérer la config du service pour l'ajouter ensuite
         */
        _getMapServiceConfig: function(element, u) {
            if (u.protocol == null) {
                return;
            }

            var title = element[this._getMappedProperty('dc:title').property];
            var id = element[this._getMappedProperty('dc:identifier').property];
            var catalog =  this._getCatalogServiceConfig(id);

            var toReturn = null;

            array.forEach(this.protocols, lang.hitch(this, function(p) {
                if (u.protocol.indexOf(p.protocol) >= 0) {
                    // on mix les configs (record config < protocol config < catalog config)
                    toReturn = {
                        priority: p.priority == null ? 0 : p.priority,
                        service: lang.mixin(lang.mixin({
                            'serviceId': id,
                            'label': title,
                            'url': u.link
                        }, p.config), catalog)
                    };
                }
            }));

            return toReturn;
        },

        /**
         * Convertit un XML en string
         * @param xml le XML à convertir
         */
        xmlToString: function(xml) {
            return new XMLSerializer().serializeToString(xml);
        },

        /**
         * Convertit un JSON en XML
         * @param json le JSON à convertir
         */
        jsonToXml: function(json) {
            return this.jsonixContext.createMarshaller().marshalDocument(json);
        },

        /**
         * Convertit un XML en JSON
         * @param xml le XML à convertir
         */
        xmlToJson: function(xml) {
            return this.jsonixContext.createUnmarshaller().unmarshalDocument(xml);
        },

        /**
         * Initialise le contexte jsonix, le mapping des propriétés et les protocoles
         */
        initContext: function() {
            this.propertiesMapping = {
                // la propriété dc:identifier (csw) est mappée sur la propriété id de notre record
                // et il ne peut y avoir qu'un seul identifier (type: 'value')
                'dc:identifier': {
                    property: 'id',
                    type: 'value',
                    label: 'Id'
                },
                'dc:type': {
                    property: 'type',
                    type: 'value',
                    label: 'Type'
                },
                'dc:date': {
                    property: 'date',
                    type: 'value',
                    label: 'Date'
                },
                'dc:title': {
                    property: 'title',
                    type: 'value',
                    label: 'Titre'
                },
                'dc:subject': {
                    property: 'subjects',
                    type: 'array',
                    label: 'Mots-clés'
                },
                'dct:abstract': {
                    property: 'abstract',
                    type: 'value',
                    label: 'Longue description'
                },
                'dc:description': {
                    property: 'description',
                    type: 'value',
                    label: 'Description'
                },
                'dc:rights': {
                    property: 'rights',
                    type: 'value',
                    label: 'Droits'
                },
                'dc:language': {
                    property: 'language',
                    type: 'value',
                    label: 'Langue'
                },
                'dc:source': {
                    property: 'source',
                    type: 'value',
                    label: 'Source'
                },
                'dct:references': {
                    property: 'references',
                    type: 'array',
                    label: 'Liens',
                    protocolProperty: 'scheme'
                }
            };

            // TODO: conf serviceProperty et protocols pour un CSW standard
            this.serviceProperty = 'dct:references';

            this.protocols = [{
                protocol: 'ESRI:REST', // protocole décrit dans le record CSW
                priority: 10, // la priorité
                config: { // la config du service correspondant à ce protocol
                    type: 'AGS_DYNAMIC',
                    hasLegend: true,
                    alpha: 100,
                    toLoad: true,
                    visible: true,
                    identifiable: true
                }
            }, {
                protocol: 'OGC:WMS',
                config: {
                    type: 'WMS',
                    hasLegend: true,
                    alpha: 100,
                    toLoad: true,
                    visible: true,
                    identifiable: false,
                    wmsParameters: {}
                }
            }];

            // jsonix contexte en lui précisant les "schémas" qui viennent de ogc-schemas
            this.jsonixContext = new Jsonix.Jsonix.Context([
                OWS_1_0_0.OWS_1_0_0,
                DC_1_1.DC_1_1,
                DCT.DCT,
                XLink_1_0.XLink_1_0,
                SMIL_2_0.SMIL_2_0,
                SMIL_2_0_Language.SMIL_2_0_Language,
                GML_3_1_1.GML_3_1_1,
                Filter_1_1_0.Filter_1_1_0,
                CSW_2_0_2.CSW_2_0_2,
                GML_3_2_0.GML_3_2_0,
                ISO19139_GSS_20060504.ISO19139_GSS_20060504,
                ISO19139_GSR_20060504.ISO19139_GSR_20060504,
                ISO19139_GTS_20060504.ISO19139_GTS_20060504,
                ISO19139_GMD_20060504.ISO19139_GMD_20060504,
                ISO19139_GCO_20060504.ISO19139_GCO_20060504,
                ISO19139_SRV_20060504.ISO19139_SRV_20060504
            ], {
                // les namespaces et leur préfixe
                namespacePrefixes: {
                    'http://www.opengis.net/cat/csw/2.0.2': 'csw',
                    'http://www.opengis.net/ogc': 'ogc',
                    'http://www.opengis.net/gml': 'gml',
                    'http://purl.org/dc/elements/1.1/':'dc',
                    'http://purl.org/dc/terms/':'dct',
                    'http://www.isotc211.org/2005/gmd' : 'gmd',
                    'http://www.isotc211.org/2005/gco' : 'gco'
                },
                mappingStyle: 'simplified'
            });
        },

        _urlGetter: function() {
            if (this.proxyPage && this.useCors !== true) {
                return this.proxyPage + '?' + this.url;
            }

            return this.url;
        },

        /**
         * JSON correspondant au XML pour l'opération GetCapabilities
         */
        _GetCapabilities: function () {
            return {
                'csw:GetCapabilities': {
                    'TYPE_NAME': 'CSW_2_0_2.GetCapabilitiesType',
                    'service': 'CSW',
                    'acceptVersions': {
                        'TYPE_NAME': 'OWS_1_0_0.AcceptVersionsType',
                        'version': ['2.0.2']
                    },
                    'acceptFormats': {
                        'TYPE_NAME': 'OWS_1_0_0.AcceptFormatsType',
                        'outputFormat': ['application/xml']
                    }
                }
            };
        },

        /**
         * JSON correspondant au XML pour l'opération GetRecords
         */
        _GetRecords: function(startPosition, maxRecords, query, outputSchema){
            var tmp = {
                'csw:GetRecords': {
                    TYPE_NAME: 'CSW_2_0_2.GetRecordsType',
                    abstractQuery: query,
                    startPosition: startPosition,
                    maxRecords: maxRecords,
                    resultType: 'results',
                    service: 'CSW',
                    version: '2.0.2'
                }
            };

            if (outputSchema) {
                tmp['csw:GetRecords'].outputSchema = outputSchema;
            }

            return tmp;
        },

        /**
         * JSON correspondant à une contrainte (filtre)
         */
        _Constraint: function(filter) {
            return {
                TYPE_NAME: "CSW_2_0_2.QueryConstraintType",
                version: "1.1.0",
                filter: filter
            };
        },

        /**
         * JSON correspondant à une Query (filter/sort)
         */
        _Query: function(elementSetName, constraint, sort) {
            var tmp = {
                'csw:Query': {
                    TYPE_NAME: 'CSW_2_0_2.QueryType',
                    elementSetName: {
                        TYPE_NAME: 'CSW_2_0_2.ElementSetNameType',
                        value: elementSetName
                    },
                    typeNames: [
                        {
                            key: '{http://www.opengis.net/cat/csw/2.0.2}Record',
                            localPart: 'Record',
                            namespaceURI: 'http://www.opengis.net/cat/csw/2.0.2',
                            prefix: 'csw',
                            string: '{http://www.opengis.net/cat/csw/2.0.2}csw:Record'
                        }
                    ]
                }
            };

            if (constraint) {
                tmp['csw:Query'].constraint = constraint;
            }

            if (sort) {
                tmp['csw:Query'].sortBy = sort;
            }

            return tmp;
        },

        /**
         * FilterBuilder pour construire le JSON du filtre facilement
         */
        FilterBuilder: declare(null, {
            constructor: function() {
                this['ogc:Filter'] = {
                    TYPE_NAME : "Filter_1_1_0.FilterType"
                };
            },

            PropertyName: function (propertyName) {
                // Temporary values
                this.tmp ={};
                // Temporary PropertyName
                this.tmp.PropertyName = propertyName;
                return this;
            },

            isLike: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsLike' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsLikeType",
                        escapeChar: "",
                        singleChar: "_",
                        wildCard: "%",
                        literal: {
                            TYPE_NAME: "Filter_1_1_0.LiteralType",
                            content: [value]
                        },
                        propertyName: {
                            TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                            content: [this.tmp.PropertyName]
                        }
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isNull: function(value) {
                throw 'Not Implemented yet';
            },

            isBetween: function(lowerValue, upperValue) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsBetween' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsBetweenType",
                        expression :{
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        },
                        lowerBoundary:{
                            expression: {
                                'ogc:Literal':{
                                    TYPE_NAME: "Filter_1_1_0.LiteralType",
                                    content :[lowerValue]
                                }
                            }
                        },
                        upperBoundary:{
                            expression: {
                                'ogc:Literal':{
                                    TYPE_NAME: "Filter_1_1_0.LiteralType",
                                    content :[upperValue]
                                }
                            }
                        }
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isEqualTo: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsEqualTo': {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsEqualTo",
                        matchCase: false,
                        expression: [{
                            'ogc:Literal': {
                                TYPE_NAME: "Filter_1_1_0.LiteralType",
                                content: [value]
                            }
                        }, {
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        }]
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isLessThanOrEqualTo: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsLessThanOrEqualTo' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsLessThanOrEqualTo",
                        expression: [{
                            'ogc:Literal': {
                                TYPE_NAME: "Filter_1_1_0.LiteralType",
                                content: [value]
                            }
                        }, {
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        }]
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isGreaterThan: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsGreaterThan' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsGreaterThan",
                        expression: [{
                            'ogc:Literal': {
                                TYPE_NAME: "Filter_1_1_0.LiteralType",
                                content: [value]
                            }
                        }, {
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        }]
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isLessThan: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsLessThan' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsLessThan",
                        expression: [{
                            'ogc:Literal': {
                                TYPE_NAME: "Filter_1_1_0.LiteralType",
                                content: [value]
                            }
                        }, {
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        }]
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isGreaterThanOrEqualTo: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsGreaterThanOrEqualTo' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsGreaterThanOrEqualTo",
                        expression: [{
                            'ogc:Literal': {
                                TYPE_NAME: "Filter_1_1_0.LiteralType",
                                content: [value]
                            }
                        }, {
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        }]
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            isNotEqualTo: function(value) {
                this['ogc:Filter'].comparisonOps = {
                    'ogc:PropertyIsNotEqualTo' : {
                        TYPE_NAME: "Filter_1_1_0.PropertyIsNotEqualTo",
                        expression: [{
                            'ogc:Literal': {
                                TYPE_NAME: "Filter_1_1_0.LiteralType",
                                content: [value]
                            }
                        }, {
                            'ogc:PropertyName': {
                                TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                                content: [this.tmp.PropertyName]
                            }
                        }]
                    }
                };
                // Delete the tmp property to prevent jsonix fail.
                delete this.tmp;
                return this;
            },

            and: function(filter) {
                if (typeof this['ogc:Filter'].logicOps === 'undefined') {
                    //console.debug('The first And');
                    this['ogc:Filter'].logicOps = {
                        'ogc:And':{
                            TYPE_NAME: "Filter_1_1_0.BinaryLogicOpType"
                            //comparisonOpsOrSpatialOpsOrLogicOps: []
                        }
                    };
                    /**
                     *   TODO We need to check if the filter/operator is a
                     *   GeometryOperands, SpatialOperators(spatialOps), ComparisonOperators
                     *   (comparisonOps), ArithmeticOperators or is a composition of them
                     *   "comparisonOpsOrSpatialOpsOrLogicOps" at the moment only supports
                     *   Filter.isLike().and(Filter.isLike()) or SpatialOps (ex: BBOX);
                     */
                    if (typeof this['ogc:Filter'].comparisonOps !== 'undefined') {
                        // Only has one previous filter and it is a comparison operator.
                        // Now is ops before was comparisonOpsOrSpatialOpsOrLogicOps
                        this['ogc:Filter'].logicOps['ogc:And'].ops = [this['ogc:Filter'].comparisonOps].concat(filter.getPreviousOperator());
                        delete this['ogc:Filter'].comparisonOps;
                    } else if (typeof this['ogc:Filter'].spatialOps !== 'undefined'){
                        // Only has one previous filter and it is a spatial operator.
                        this['ogc:Filter'].logicOps['ogc:And'].ops = [this['ogc:Filter'].spatialOps].concat(filter.getPreviousOperator());
                        delete this['ogc:Filter'].spatialOps;
                    } else {
                        throw 'Not Implemented yet, another operators';
                    }
                } else {
                    // It has two or more previous operators. TODO They must be And Operator fix to accept 'ogc:Or'.
                    this['ogc:Filter'].logicOps['ogc:And'].ops = this['ogc:Filter'].logicOps['ogc:And'].ops.concat(filter.getPreviousOperator());
                }
                return this;
            },

            or: function(filter) {
                if (typeof this['ogc:Filter'].logicOps === 'undefined') {
                    //console.debug('The first Or');
                    this['ogc:Filter'].logicOps = {
                        'ogc:Or':{
                            TYPE_NAME: "Filter_1_1_0.BinaryLogicOpType"
                            //comparisonOpsOrSpatialOpsOrLogicOps: []
                        }
                    };
                    /**
                     *   TODO We need to check if the filter/operator is a
                     *   GeometryOperands, SpatialOperators(spatialOps), ComparisonOperators
                     *   (comparisonOps), ArithmeticOperators or is a composition of them
                     *   "comparisonOpsOrSpatialOpsOrLogicOps" at the moment only supports
                     *   Filter.isLike().and(Filter.isLike()) or SpatialOps (ex: BBOX);
                     */
                    if (typeof this['ogc:Filter'].comparisonOps !== 'undefined') {
                        // Only has one previous filter and it is a comparison operator.
                        this['ogc:Filter'].logicOps['ogc:Or'].ops = [this['ogc:Filter'].comparisonOps].concat(filter.getPreviousOperator());
                        delete this['ogc:Filter'].comparisonOps;
                    } else if (typeof this['ogc:Filter'].spatialOps !== 'undefined'){
                        // Only has one previous filter and it is a spatial operator.
                        this['ogc:Filter'].logicOps['ogc:Or'].ops = [this['ogc:Filter'].spatialOps].concat(filter.getPreviousOperator());
                        delete this['ogc:Filter'].spatialOps;
                    } else {
                        throw 'Not Implemented yet, another operators';
                    }
                } else {
                    // It has two or more previous operators. TODO They must be And Operator fix to accept 'ogc:And'.
                    this['ogc:Filter'].logicOps['ogc:Or'].ops = this['ogc:Filter'].logicOps['ogc:Or'].ops.concat(filter.getPreviousOperator());
                }
                return this;
            },

            not: function(filter) {
                throw 'Not Implemented yet';
            },

            getPreviousOperator: function() {
                var operator;
                var filter = this;

                if (typeof filter['ogc:Filter'].comparisonOps !== 'undefined') {
                    // Only has one previous filter and it is a comparison operator.
                    operator = filter['ogc:Filter'].comparisonOps;
                } else if (typeof filter['ogc:Filter'].spatialOps !== 'undefined'){
                    // Only has one previous filter and it is a spatial operator.
                    operator = filter['ogc:Filter'].spatialOps;
                } else if (typeof filter['ogc:Filter'].logicOps !== 'undefined') {
                    operator = filter['ogc:Filter'].logicOps;
                } else {
                    console.error(filter);
                    throw 'Not Implemented yet, another operators';
                }
                return operator;
            },

            BBOX: function(llat, llon, ulat, ulon, srsName) {
                this['ogc:Filter'].spatialOps = {
                    'ogc:BBOX' : {
                        TYPE_NAME: "Filter_1_1_0.BBOXType",
                        envelope :{
                            'gml:Envelope' : {
                                TYPE_NAME: "GML_3_1_1.EnvelopeType",
                                lowerCorner: {
                                    TYPE_NAME: "GML_3_1_1.DirectPositionType",
                                    value : [llat, llon]
                                },
                                upperCorner : {
                                    TYPE_NAME: "GML_3_1_1.DirectPositionType",
                                    value : [ulat, ulon]
                                },
                                srsName: srsName
                            }
                        },
                        propertyName :{
                            TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                            content: "ows:BoundingBox"
                        }
                    }
                };
                return this;
            }
        }),

        /**
         * SortBuiler pour construire le JSON du sort facilement
         */
        SortBuilder: declare(null, {
            constructor: function() {
                this['ogc:SortBy'] = {
                    TYPE_NAME : "Filter_1_1_0.SortByType"
                };
            },

            Sort: function(propertyName, desc) {
                this['ogc:SortBy'].sortProperty = this['ogc:SortBy'].sortProperty || [];

                this['ogc:SortBy'].sortProperty.push({
                    TYPE_NAME: "Filter_1_1_0.SortPropertyType",
                    propertyName: {
                        TYPE_NAME: "Filter_1_1_0.PropertyNameType",
                        content: [propertyName]
                    },
                    sortOrder: desc ? 'DESC': 'ASC'
                });

                return this;
            }
        })

    });
});