source : layersService.js

(function (angular) {
    'use strict';
    /**
     * @memberof spApp
     * @ngdoc service
     * @name LayersService
     * @description
     *   Access to layer services of spatial-service
     */
    angular.module('layers-service', ['ngFileUpload'])
        .factory('LayersService', ['$http', '$timeout', '$q', 'Upload','gLayers', function ($http, $timeout, $q, Upload, gLayers) {
            var layers = gLayers;
            var _httpDescription = function (method, httpconfig) {
                if (httpconfig === undefined) {
                    httpconfig = {};
                }
                httpconfig.service = 'LayersService';
                httpconfig.method = method;
                return httpconfig;
            };
            var thiz = {
                /**
                 * Search a pageable list of field objects
                 * @memberof LayersService
                 * @param {String} field field
                 * @param {integer} offset start index
                 * @param {integer} pageSize page size
                 * @param {String} searchTerm search term
                 * @returns {Promise(List)} list of field objects
                 *
                 * @example
                 * Output:
                 * {
                        "enabled": true,
                        "objects": [{
                            "pid": "3742600",
                            "wmsurl": "https://spatial.ala.org.au/geoserver/wms?service=WMS&version=1.1.0&request=GetMap&layers=ALA:Objects&format=image/png&viewparams=s:3742600",
                            "area_km": 986564.4158071108,
                            "fid": "cl22",
                            "bbox": "POLYGON((128.999924 -38.062248,128.999924 -25.9959109999999,141.001709 -25.9959109999999,141.001709 -38.062248,128.999924 -38.062248))",
                            "centroid": "POINT(135.82719986113 -30.1046824043507)",
                            "fieldname": "Australian States and Territories",
                            "featureType": "MULTIPOLYGON",
                            "description": "South Australia, State",
                            "name": "South Australia",
                            "id": "South Australia"
                            }],
                        "spid": "22",
                        "indb": true,
                        "last_update": 1317906000000,
                        "namesearch": true,
                        "sdesc": "name_1,type_1",
                        "sid": "name_1",
                        "intersect": true,
                        "layerbranch": true,
                        "analysis": true,
                        "addtomap": true,
                        "number_of_objects": 11,
                        "sname": "name_1",
                        "defaultlayer": true,
                        "desc": "Australian States and Territories",
                        "name": "Australian States and Territories",
                        "id": "cl22",
                        "type": "c"
                        }
                 *
                 */
                getField: function (field, start, max, q) {
                    if (q === undefined) q = '';
                    var url = this.url() + "/field/" + field + "?start=" + start + "&pageSize=" + max + "&q=" + encodeURIComponent(q);
                    return $http.get(url, _httpDescription('getField'))
                },
                /**
                 * List all fields. See #searchLayers for output example
                 * @memberof LayersService
                 * @returns {Promise(List)} list of fields
                 */
                getLayers: function () {
                    var url = this.url() + "/fields/search?q=";
                    return $http.get(url, _httpDescription('getField'))
                },
                /**
                 * Search spatial-service layers
                 * @memberof LayersService
                 * @param {String} searchTerm search term
                 * @returns {Promise(List)} list of layers
                 *
                 * @example
                 * Input:
                 *  "capad"
                 *
                 * Output:
                 *  [{
                     "enabled": true,
                     "spid": "2080",
                     "indb": true,
                     "last_update": 1426683600000,
                     "namesearch": true,
                     "sid": "name",
                     "intersect": false,
                     "layerbranch": false,
                     "analysis": true,
                     "addtomap": true,
                     "layer": {
                         "displayname": "CAPAD 2012 Marine",
                         "enabled": true,
                         "pid": "",
                         "displaypath": "https://spatial.ala.org.au/geoserver/gwc/service/wms?service=WMS&version=1.1.0&request=GetMap&layers=ALA:capad_2012_marine&format=image/png&style=cl2080_style",
                         "uid": "2080",
                         "metadatapath": "http://www.environment.gov.au/fed/catalog/search/resource/details.page?uuid=%7B9F45DF80-CB86-440C-98DD-0B206B86D712%7D",
                         "classification1": "Area Management",
                         "classification2": "Biodiversity",
                         "notes": "",
                         "source_link": "http://www.environment.gov.au/fed/catalog/search/resource/downloadData.page?uuid=%7B9F45DF80-CB86-440C-98DD-0B206B86D712%7D",
                         "licence_link": "http://www.environment.gov.au/fed/catalog/search/resource/details.page?uuid=%7B9F45DF80-CB86-440C-98DD-0B206B86D712%7D",
                         "licence_notes": "",
                         "maxlatitude": -8.88189,
                         "minlatitude": -58.44947,
                         "minlongitude": 70.9,
                         "maxlongitude": 170.36667,
                         "shape": true,
                         "path_orig": "shape/capad_2012_marine",
                         "environmentalvalueunits": "",
                         "domain": "marine",
                         "dt_added": 1426683600000,
                         "environmentalvaluemin": "",
                         "lookuptablepath": "",
                         "citation_date": "2012-12-31",
                         "datalang": "eng",
                         "licence_level": "3",
                         "mddatest": "2014-12-05",
                         "mdhrlv": "",
                         "respparty_role": "custodian",
                         "keywords": "IUCN, reserve, park, conservation, wilderness",
                         "path_1km": "",
                         "environmentalvaluemax": "",
                         "description": "CAPAD (Collaborative Australian Protected Area Database) contains information on all protected areas in Australia, including their IUCN management categories.",
                         "source": "http://www.environment.gov.au/land/nrs/science/capad/2012",
                         "scale": "",
                         "name": "capad_2012_marine",
                         "id": 2080,
                         "type": "Contextual",
                         "path": "/data/ala/data/layers/ready/shape"
                         },
                     "sname": "name",
                     "defaultlayer": true,
                     "desc": "capad 2012 marine",
                     "name": "CAPAD 2012 Marine",
                     "id": "cl2080",
                     "type": "c"
                     }]
                 */
                searchLayers: function (q) {
                    var url = this.url() + '/fields/search?q=' + q;
                    return $http.get(url, _httpDescription('searchLayers'))
                },
                /**
                 * Intersect layers with a single point
                 * @memberof LayersService
                 * @param {List} layers list of layers
                 * @param {Number} longitude
                 * @param {Number} latitude
                 * @returns {Promise(List)} intersection results
                 *
                 * @example
                 * Input:
                 * - layers
                 *  ["cl22", "cl23"]
                 * - lng
                 *  131
                 * - lat
                 *  -22
                 *
                 * Output:
                 * [{
                     "field": "cl22",
                     "layername": "Australian States and Territories",
                     "value": ""
                     },
                 {
                 "field": "cl23",
                 "layername": "LGA Boundaries (deprecated)",
                 "value": ""
                 }]
                 */
                intersectLayers: function (layers, lng, lat) {
                    if (layers.length > 0 && layers[0] !== undefined) {
                        return $http.get(this.url() + '/intersect/' + layers.join() + "/" + lat + "/" + lng, _httpDescription('intersectLayers'))
                    } else {
                        return $q.when()
                    }
                },
                /**
                 * Get layer information
                 * @memberof LayersService
                 * @param {string} layer Layer name or field id.
                 * @returns {Map} Layer information. see #getLayers
                 */
                getLayer: function (layer) {
                    for (var i = 0; i < layers.length; i++) {
                        if (layers[i].id === layer || layers[i].layer.name.toLowerCase() === layer.toLowerCase()) {
                            return layers[i];
                        }
                    }
                },
                /**
                 * Get layer information without conflict
                 * @memberof LayersService
                 * @param {string} layer Layer name or field id.
                 * @returns {Map} Layer information. see #getLayers
                 */
                getLayersUrlLoad: function (layer) {
                    //TODO: refactor LayersService, search and 'layers' with promises
                    return this.getLayers().then(function (response) {
                        var layers = response.data;
                        for (var i = 0; i < layers.length; i++) {
                            if (layers[i].id === layer || layers[i].layer.name.toLowerCase() === layer.toLowerCase()) {
                                return layers[i];
                            }
                        }
                    });
                },
                /**
                 * Create layer using WKT
                 * @memberof LayersService
                 * @param {string} WKT
                 * @param {string} name display name
                 * @param {string} description a description
                 * @returns {Promise(Map)} created area information
                 *
                 * @example
                 * { TODO: example }
                 */
                createFromWkt: function (wkt, name, description) {
                    return $http.post($SH.baseUrl + '/portal/postAreaWkt',
                        {
                            wkt: wkt,
                            name: name,
                            description: description,
                            user_id: $SH.userId
                        })
                },
                /**
                 * Get object information
                 * @memberof LayersService
                 * @param {string} pid object id
                 * @returns {Promise(Map)} object information
                 *
                 * @example
                 * { TODO: example }
                 */
                getObject: function (id) {
                    return $http.get(this.url() + '/object/' + id, _httpDescription('getObject'))
                },
                /**
                 * Get objects for a layer (field)
                 * @memberof LayersService
                 * @param {string} object id
                 * @returns {Promise(List)} object information
                 *
                 * @example
                 * { TODO: example }
                 */
                getObjects: function (id) {
                    return $http.get(this.url() + '/objects/' + id, _httpDescription('getObjects'))
                },
                /**
                 * Get object WKT
                 * @memberof LayersService
                 * @param {string} pid object id
                 * @returns {Promise(String)} WKT
                 */
                getWkt: function (id) {
                    return $http.get(this.url() + '/shape/wkt/' + id, _httpDescription('getWkt'))
                },
                /**
                 * Get spatial-service URL
                 * @memberof LayersService
                 * @returns {String} spatial-service URL
                 */
                url: function () {
                    return $SH.layersServiceUrl
                },
                /**
                 * Get the spatial-service gazetteer field id
                 * @memberof LayersService
                 * @returns {String} field id
                 */
                gazField: function () {
                    return $SH.gazField
                },
                /**
                 * Get URL to download an object shapefile, kml or WKT
                 * @memberof LayersService
                 * @param {string} pid object id
                 * @param {string} type download type, one of 'shp', 'kml' or 'wkt'
                 * @param {string} filename the name of the file to download. Note that type == 'shp' produces a .zip
                 * @returns {String} URL
                 */
                getAreaDownloadUrl: function (pid, type, filename) {
                    return $SH.layersServiceUrl + "/shape/" + type + "/" + pid + "?filename=" + encodeURIComponent(filename)
                },
                /**
                 * Get URL to image for an incomplete shapefile upload selection
                 * @memberof LayersService
                 * @param {string} shapeId shape id
                 * @param {string} pid selected area id
                 * @returns {String} URL to selection image
                 */
                getShpImageUrl: function (shapeId, selectedArea) {
                    if (selectedArea.length > 0) {
                        return this.url() + '/shape/upload/shp/image/' + shapeId + "/" + selectedArea;
                    } else {
                        return this.url() + '/shape/upload/shp/image/' + shapeId + "/all";
                    }
                },
                /**
                 * Upload an area file. zip (shapefile) or kml
                 * TODO: support zipped kml
                 * @memberof LayersService
                 * @param {File} file
                 * @param {string} type one of 'shp' or 'kml'
                 * @param {string} name area name
                 * @param {string} description a description
                 * @returns {Promise(Map)} in progress uploaded area info
                 */
                uploadAreaFile: function (file, type, name, desc) {
                    var uploadType = "shp";
                    if (type === 'importKML') {
                        uploadType = "kml";
                    }
                    var uploadURL = $SH.baseUrl + "/portal/postAreaFile/" + uploadType + "?name=" + name + "&description=" + desc;
                    file.upload = Upload.upload({
                        url: uploadURL,
                        data: {shapeFile: file}
                    });
                    return file.upload;
                },
                /**
                 * Create an area from an in progress shapefile upload
                 * @memberof LayersService
                 * @param {string} name area name
                 * @param {string} description a description
                 * @param {string} shapeId shape id for the uploaded file
                 * @param {string} featureIdx comma delimited list of feature ids to merge for the created area
                 * @returns {Promise(Area)} created area
                 */
                createArea: function (name, description, shpId, featureIdx) {
                    var param = {
                        name: name,
                        description: description,
                        shpId: shpId,
                        featureIdx: featureIdx
                    };
                    return $http.post($SH.baseUrl + '/portal/postArea', param, _httpDescription('createArea'));
                },
                /**
                 * Search for areas associated with an LSID.
                 * @param {string} type 'distribution', 'checklist', or 'track'
                 * @param {string} LSID
                 * @returns {Promise}
                 */
                findOtherArea: function (type, lsid, area) {
                    return $http.get(this.url() + '/' + type + '/lsids/' + lsid + '?nowkt=true', _httpDescription('findOtherArea', {ignoreErrors: true}))
                },
                /**
                 * Create Layer for mapping using field data
                 * @param {string} fieldData field data
                 * @param {boolean} isSelected default selection value
                 * @returns {Promise(Layer)} layer for use in MapService#add
                 */
                convertFieldDataToMapLayer: function (fieldData, isSelected) {
                    return {
                        id: fieldData.id,
                        classification1: fieldData.layer.classification1,
                        classification2: fieldData.layer.classification2,
                        classification: fieldData.layer.classification1 + ' / ' + fieldData.layer.classification2,
                        name: fieldData.name,
                        type: fieldData.type,
                        dist: 2,
                        selected: isSelected,
                        layerId: fieldData.layer.id,
                        bbox: [[fieldData.layer.minlatitude, fieldData.layer.minlongitude], [fieldData.layer.maxlatitude, fieldData.layer.maxlongitude]],
                        shortName: fieldData.layer.name,
                        layerType: fieldData.layer.type,
                        analysis: fieldData.analysis
                    }
                },
                /**
                 * Create Layer for mapping using field id
                 * @param {string} fieldId field data
                 * @param {boolean} isSelected default selection value
                 * @returns {Promise(Layer)} layer for use in MapService#add
                 */
                convertFieldIdToMapLayer: function (fieldId, isSelected) {
                    var fieldData = this.getLayer(fieldId);
                    return this.convertFieldDataToMapLayer(fieldData, isSelected)
                },
                /**
                 * Query layers using WMS service getFeatureInfo.
                 *
                 * @param layers
                 * @param latlng
                 * @returns {HttpPromise}
                 */
                getFeatureInfo: function (layers, leafletMap, latlng) {
                    // TODO: support >1 WMS sources
                    var url = layers[0].leaflet.layerOptions.layers[0].url;
                    var layerNames = '';
                    for (var ly in layers) {
                        if (layerNames.length > 0) layerNames += ',';
                        layerNames += layers[ly].leaflet.layerOptions.layers[0].layerOptions.layers
                    }
                    var point = leafletMap.latLngToContainerPoint(latlng, leafletMap.getZoom());
                    var size = leafletMap.getSize();
                    var crs = leafletMap.options.crs;
                    var sw = crs.project(leafletMap.getBounds().getSouthWest());
                    var ne = crs.project(leafletMap.getBounds().getNorthEast());
                    var params = {
                        request: 'GetFeatureInfo',
                        srs: crs.code,
                        bbox: sw.x + ',' + sw.y + ',' + ne.x + ',' + ne.y,
                        height: size.y,
                        width: size.x,
                        layers: layerNames,
                        query_layers: layerNames,
                        feature_count: layers.length * 10,
                        info_format: 'text/plain',
                        x: point.x,
                        i: point.x,
                        y: point.y,
                        j: point.y
                    };
                    var split = url.split('?');
                    var urlBase = split[0] + "?";
                    var existingParams = '';
                    if (split.length > 1) {
                        existingParams = split[1].split('&');
                    }
                    for (var i in existingParams) {
                        if (existingParams[i].match(/^layers=.*/) == null) {
                            urlBase += '&' + existingParams[i];
                        }
                    }
                    url = urlBase.replace("/gwc/service", "") + L.Util.getParamString(params, urlBase, true);
                    return $http.get(url, _httpDescription('getFeatureInfo')).then(function (response) {
                        return thiz.parseGetFeatureInfo(response.data, layers)
                    })
                },
                /**
                 * Test if an area intersects with a coordinate
                 *
                 * @memberOf PopupService
                 * @param {list} pid area id
                 * @param {latlng} latlng coordinate to inspect as {lat:latitude, lng:longitude}
                 * @returns {HttpPromise}
                 *
                 * @example
                 * Input:
                 * - pid
                 *  67620
                 * - latlng
                 *  {lat:-22, lng:131}
                 * Output:
                 * {
                            "name_id": 0,
                            "pid": "67620",
                            "id": "Northern Territory",
                            "fieldname": "Australian States and Territories",
                            "featureType": "MULTIPOLYGON",
                            "area_km": 1395847.4575625565,
                            "description": "null",
                            "bbox": "POLYGON((128.999222 -26.002015,128.999222 -10.902499,137.996094 -10.902499,137.996094 -26.002015,128.999222 -26.002015))",
                            "fid": "cl22",
                            "wmsurl": "https://spatial.ala.org.au/geoserver/wms?service=WMS&version=1.1.0&request=GetMap&layers=ALA:Objects&format=image/png&viewparams=s:67620",
                            "name": "Northern Territory"
                        }
                 */
                getAreaIntersects: function (pid, latlng) {
                    var url = $SH.layersServiceUrl + "/object/intersect/" + pid + "/" + latlng.lat + "/" + latlng.lng;
                    return $http.get(url, _httpDescription('getAreaIntersects'))
                },
                parseGetFeatureInfo: function (plainText, layers) {
                    var result = [];
                    var blockString = "--------------------------------------------";
                    for (var ly in layers) {
                        var layerName = layers[ly].leaflet.layerOptions.layers[0].layerOptions.layers;
                        layerName = layerName.replace("ALA:", "");
                        var field = this.getLayer(layers[ly].id + '');
                        var sname;
                        if (field) {
                            sname = field.sname;
                        }
                        var units = undefined;
                        //start of layer intersect values in response from geoserver is "http.*{{layerName}}':"
                        var start = plainText.indexOf(layerName + "':");
                        var value = '';
                        if (start > 0) {
                            var blockStart = plainText.indexOf(blockString, start);
                            var blockEnd = plainText.indexOf(blockString, blockStart + blockString.length);
                            var properties = plainText.substring(blockStart + blockString.length, blockEnd - 1).trim().split("\n")
                            if (sname) {
                                for (var i in properties) {
                                    if (properties[i].toUpperCase().match('^' + sname.toUpperCase() + ' = .*') != null) {
                                        value = properties[i].substring(properties[i].indexOf('=') + 2, properties[i].length).trim();
                                    }
                                }
                            } else {
                                value = properties[0].substring(properties[0].indexOf('=') + 2, properties[0].length).trim();
                                if (isNaN(value)) {
                                    // use value as-is
                                } else if (field) {
                                    // filter out nodatavalues
                                    units = field.layer.environmentalvalueunits;
                                    if (Number(value) < Number(field.layer.environmentalvaluemin)) {
                                        value = '';
                                    }
                                } else {
                                    // analysis layers have nodatavalue < 0
                                    if (Number(value) < 0) {
                                        value = ''
                                    }
                                }
                            }
                        }
                        // no intersect with this layer
                        if (units) {
                            result.push({
                                layername: layers[ly].name,
                                value: value,
                                units: units
                            })
                        } else {
                            result.push({
                                layername: layers[ly].name,
                                value: value
                            })
                        }
                    }
                    return result;
                }
            }
            return thiz;
        }])
}(angular));