UNPKG

arc-node

Version:

Node module to work with ArcGIS Online and ArcGIS Server

746 lines (612 loc) 20.8 kB
/** * Created by Raul.Jimenez on 17/07/2015. */ 'use strict'; var defer = require('node-promise').defer; var querystring = require('querystring'); var ArcJSON = require('arcgis-json-objects'); var https = require('https'); var http = require('http'); var StringDecoder = require('string_decoder').StringDecoder; module.exports = function ArcNode(options) { options = options || {}; this.username = options.username || ''; this.password = options.password || ''; this.account_id = options.account_id || ''; this.root_url = options.root_url || ''; this.services_url = options.services_url || ''; this.port = options.port || 443; this.arcgisPath = options.arcgisPath || ''; this.portalPath = options.portalPath || ''; this.allowSelfSigned = options.allowSelfSigned || false; this.print_service = options.print_service || 'http://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task'; this.find_address_candidates_service = options.find_address_candidates_service || 'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates'; var that = this; /************************************************************ * * This function renews the instance token for 15 days * ************************************************************/ this.getToken = function(options){ var deferred = defer(), postData; options = options || {}; options.client = options.client || 'referer'; options.referrer = options.expiration || 'arcgis.com'; options.expiration = options.expiration || 21600; postData = querystring.stringify({ f: 'json', username: that.username, password: that.password, client: options.client, referer: options.referrer, expiration: options.expiration }); if(that.allowSelfSigned){ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; } var req = { hostname: that.root_url, port: that.port, path: '/' + that.arcgisPath + '/sharing/rest/generateToken', method: 'POST', body: postData, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; //console.log('req = ', req); req = https.request(req, function(response) { response.setEncoding('utf8'); response.on('data', function (chunk) { try{ chunk = JSON.parse(chunk); that.token = chunk.token; }catch(e){ console.log("Error: ", e); } deferred.resolve(chunk); }).on('error', function(e) { deferred.reject(e.message); }); }); req.write(postData); req.end(); return deferred; }; /************************************************************ * * This function gets user info, if admin token is passed * it will return private user info as email, groups, etc * ************************************************************/ this.getUserInfo = function(options){ var deferred = defer(), req, obj = '', decoder, textChunk; options = options || {}; options.token = options.token || that.token; options.username = options.username || that.username; var url = 'https://' + that.root_url+'/'+that.portalPath+'/sharing/rest/community/users/' + options.username + '?f=json&token=' + that.token; //console.log('url = ', url); req = https.get(url, function(res) { res.on('data', function(chunk) { res.setEncoding('utf8'); decoder = new StringDecoder('utf8'); textChunk = decoder.write(chunk); obj += textChunk; if(chunk.error && chunk.error.code == 498){ that.getToken().then(function(response){ that.getUserInfo(options).then(function(user){ deferred.resolve(user); }); }, function(e) { deferred.reject(e.message); }); } }); res.on('end', function(chunk) { obj = JSON.parse(obj); deferred.resolve(obj); }); }).on('error', function(e) { deferred.reject(e.message); }); req.end(); return deferred; }; /************************************************************ * * This function checks if s feature service exists * ************************************************************/ this.checkIfFSExists = function(options){ var deferred = defer(); var req; var url = 'https://' + that.root_url+'/sharing/rest/portals/' + that.account_id+'/isServiceNameAvailable?name=' + options.serviceName+'&f=json&type=Feature Service&token=' + that.token; req = https.get(url, function(res) { res.on('data', function(chunk) { try{ chunk = JSON.parse(chunk); }catch(e){ console.log("Error: ", e); } if(chunk.error && chunk.error.code == 498){ that.getToken().then(function(response){ that.checkIfFSExists(options).then(function(available){ deferred.resolve(available); }); }, function(e) { deferred.reject(e.message); }); }else{ deferred.resolve(chunk); } }); }).on('error', function(e) { deferred.reject(e.message); }); req.end(); return deferred; }; /************************************************************ * * This function create an empty Feature Service * ************************************************************/ this.createFeatureService = function(options){ var deferred = defer(); var fs = ArcJSON.featureService(options); var postData = querystring.stringify({ createParameters: JSON.stringify(fs), targetType: 'featureService', token: that.token, f: 'json' }); var optionsReq = { host: that.root_url, path: '/sharing/rest/content/users/'+that.username+'/createService', method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; var req = http.request(optionsReq, function(res) { //var decoder = new StringDecoder('utf8'); res.on('data', function(chunk) { //var textChunk = decoder.write(chunk); try{ chunk = JSON.parse(chunk); }catch(e){ console.log("Error: ", e); } //console.log("chunk=",textChunk) deferred.resolve(chunk); }); }).on('error', function(e) { deferred.reject(e.message); }); req.write(postData); req.end(); return deferred; }; /************************************************************ * * This function adds a Layer to a Feature service * ************************************************************/ this.addLayersToFS = function(options){ var host, path, postData, deferred, optionsReq, req, aux, decoder, textChunk, obj = ''; deferred = defer(); options.service = decodeURI(options.service); aux = options.service.split('/'); host = aux[2]; aux.shift(); aux.shift(); aux.shift(); aux.pop(); aux = '/' + aux.join('/'); path = aux.replace('/rest/','/admin/') + '.FeatureServer/addToDefinition'; postData = querystring.stringify({ addToDefinition: JSON.stringify({layers: options.layers}), token: that.token, f: 'json' }); optionsReq = { host: host, path: encodeURI(path), method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; req = http.request(optionsReq, function(res) { res.on('data', function(chunk) { res.setEncoding('utf8'); decoder = new StringDecoder('utf8'); textChunk = decoder.write(chunk); obj += textChunk; if(chunk.error && chunk.error.code == 498){ that.getToken().then(function(response){ that.getUserInfo(options).then(function(user){ deferred.resolve(user); }); }, function(e) { deferred.reject(e.message); }); } }); res.on('end', function(chunk) { try{ obj = JSON.parse(obj); if(obj.code == 400){ console.log('Error: ',obj); } deferred.resolve(obj); }catch(e){ console.log('optionsReq: ', optionsReq); console.log('Error: ', obj); deferred.reject(e.message); } }); }).on('error', function(e) { deferred.reject(e.message); }); req.write(postData); req.end(); return deferred; }; /************************************************************ * * This methods gets some features to a given layer in a * feature service. * ************************************************************/ this.getFeatures = function (options) { var req, deferred, path, requestOptions, postData, decoder, textChunk, obj, query; deferred = defer(); query = options.query; if(!query.hasOwnProperty('token')){ query.token = that.token; } query = Object.keys(query).map(function(k) { return encodeURIComponent(k) + '=' + encodeURIComponent(query[k]); }).join('&'); if(options.serviceName && options.layer){ path = 'https://'+that.services_url+'/' + that.account_id + '/arcgis/rest/services/'+ options.serviceName + '/FeatureServer/' + options.layer; }else if(options.serviceUrl){ path = options.serviceUrl; } path += '/query?' + query; obj = ''; path = path.replace('http://', 'https://'); //console.log('path=',path); req = https.get(path, function (res) { res.on('data', function (chunk) { res.setEncoding('utf8'); decoder = new StringDecoder('utf8'); textChunk = decoder.write(chunk); obj += textChunk; //console.log('Recibida trama, textChunk=',textChunk); if (chunk.error && chunk.error.code == 498) { //console.log('Error: invalid token'); that.getToken().then(function(){ that.getFeatures(options); }); } }); res.on('end', function (chunk) { //console.log('terminado, chunk=',chunk); obj = JSON.parse(obj); deferred.resolve(obj); }); }).on('error', function (e) { console.log('Problem with request: ', e.message); deferred.reject(e); }); req.end(); return deferred.promise; }, /************************************************************ * * This methods gets some features to a given layer in a * feature service. * ************************************************************/ this.getFeaturesType = function (options) { var req, deferred, path, requestOptions, postData, decoder, textChunk, obj, query; deferred = defer(); query = options.query; if(!query.hasOwnProperty('token')){ query.token = that.token; } query = Object.keys(query).map(function(k) { return encodeURIComponent(k) + '=' + encodeURIComponent(query[k]); }).join('&'); if(options.serviceName && options.layer){ path = 'https://'+that.services_url+'/' + that.account_id + '/arcgis/rest/services/'+ options.serviceName + '/FeatureServer/' + options.layer; }else if(options.serviceUrl){ path = options.serviceUrl; } path += '/?' + query; obj = ''; //console.log('path=',path); path = path.replace('http:','https:'); //console.log('path=',path); req = https.get(path, function (res) { res.on('data', function (chunk) { res.setEncoding('utf8'); decoder = new StringDecoder('utf8'); textChunk = decoder.write(chunk); obj += textChunk; //console.log('Recibida trama, textChunk=',textChunk); if (chunk.error && chunk.error.code == 498) { //console.log('Error: invalid token'); that.getToken().then(function(){ that.getFeatures(options); }); } }); res.on('end', function (chunk) { //console.log('terminado, chunk=',chunk); try{ obj = JSON.parse(obj); }catch(e){ console.log("Error: ", e); } deferred.resolve(obj); }); }).on('error', function (e) { console.log('Problem with request: ', e.message); deferred.reject(e); }); req.end(); return deferred.promise; }, /************************************************************ * * This methods adds some features to a given layer in a * feature service. * ************************************************************/ this.addFeatures = function (options) { var req, deferred, path, requestOptions, postData, decoder, textChunk, obj = ''; deferred = defer(); postData = querystring.stringify({ f: 'json', features: JSON.stringify(options.features), token: that.token }); if(options.serviceName && options.layer != undefined){ path = '/' + that.account_id + '/arcgis/rest/services/'+ options.serviceName + '/FeatureServer/' + options.layer; }else if(options.serviceUrl){ path = options.serviceUrl; } path = decodeURI(path); path += '/addFeatures?token=' + that.token; requestOptions = { host: that.services_url, path: encodeURI(path), method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; req = http.request(requestOptions, function (res) { res.setEncoding('utf8'); res.on('data', function(chunk) { res.setEncoding('utf8'); decoder = new StringDecoder('utf8'); textChunk = decoder.write(chunk); obj += textChunk; if(chunk.error && chunk.error.code == 498){ that.getToken().then(function(response){ that.getUserInfo(options).then(function(user){ deferred.resolve(user); }); }, function(e) { deferred.reject(e.message); }); } }); res.on('end', function(chunk) { try{ chunk = JSON.parse(obj); if (chunk.error && chunk.error.code == 498) { //console.log('Error: invalid token'); that.getToken().then(function(){ that.addFeatures(options); }); } else { deferred.resolve(chunk); } }catch(e){ console.log('requestOptions: ', requestOptions); console.log('Error: ', obj); deferred.reject(e.message); } }); }); req.on('error', function (e) { console.log('Problem with request: ', e.message); deferred.reject(e); }); req.write(postData); req.end(); return deferred; }, /************************************************************ * * This methods update some features to a given layer in a * feature service. * ************************************************************/ this.updateFeatures = function (options) { var req, deferred, path, requestOptions, postData; deferred = defer(); postData = querystring.stringify({ f: 'json', features: JSON.stringify(options.features), token: that.token }); if(options.serviceName && options.layer){ path = '/' + that.account_id + '/arcgis/rest/services/'+ options.serviceName + '/FeatureServer/' + options.layer; }else if(options.serviceUrl){ //path = options.serviceUrl; path = options.serviceUrl.split('/'); path = path.splice(3); path = '/' + path.join('/'); } path += '/updateFeatures?token=' + that.token; requestOptions = { host: that.services_url, path: encodeURI(path), method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; //console.log('requestOptions = ',requestOptions); req = http.request(requestOptions, function (response) { response.setEncoding('utf8'); response.on('data', function (chunk) { try{ chunk = JSON.parse(chunk); }catch(e){ console.log("Error: ", e); } if (chunk.error && chunk.error.code == 498) { //console.log('Error: invalid token'); that.getToken().then(function(){ that.updateFeatures(options); }); } else { deferred.resolve(chunk); } }); }); req.on('error', function (e) { console.log('Problem with request: ', e.message); deferred.reject(e); }); req.write(postData); req.end(); return deferred; }, /************************************************************ * * This methods returns candidates for an address using * find_address_candidates_service * ************************************************************/ this.findAddressCandidates = function (options) { var xy, deferred, path, query; if(options.x && options.y) { xy = [options.x, options.y]; return { then: function(callback){ callback(xy); } }; }else if(options.address){ deferred = defer(); query = { SingleLine: options.address, f: 'json', outSR: '{"wkid":102100}', outFields: 'Match_addr', Addr_type: 'StAddr,City', maxLocations:1 }; query = Object.keys(query).map(function(k) { return encodeURIComponent(k) + '=' + encodeURIComponent(query[k]); }).join('&'); path = this.find_address_candidates_service + '?' + query; path = path.replace('http://', 'https://'); https.get(path, function(response) { response.setEncoding('utf8'); response.on('data', function (chunk) { try{ chunk = JSON.parse(chunk); }catch(e){ console.log("Error: ", e); } deferred.resolve(chunk); }); }).on('error', function(e) { console.log('Got error: ' + e.message); deferred.reject(e); }); return deferred.promise; }else{ return {error: 'You should specify a longitude & latitude or an address'}; } }, /************************************************************ * * This methods execute print_service, by default the * Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task * ************************************************************/ this.ExportWebMapTask = function (options) { var postData, deferred, requestOptions, req; deferred = defer(); postData = querystring.stringify({ f: options.f || 'json', format: options.format || 'PNG32', layout_template: options.layoutTemplate || 'MAP_ONLY', web_map_as_json: JSON.stringify(options.webmap) }); requestOptions = { host: this.getLocation(this.print_service).host, path: this.getLocation(this.print_service).pathname + '/execute', method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; req = http.request(requestOptions, function (response) { response.setEncoding('utf8'); response.on('data', function (chunk) { try{ chunk = JSON.parse(chunk); }catch(e){ console.log("Error: ", e); } deferred.resolve(chunk); }); }); req.on('error', function (e) { console.log('Problem with request: ', e.message); deferred.reject(e); }); req.write(postData); req.end(); return deferred; }, /************************************************************ * * This methods returns a JSON object with the different * parts of the URL (host, pathname, etc.) * ************************************************************/ this.getLocation = function(url){ var match = url.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)(\/[^?#]*)(\?[^#]*|)(#.*|)$/); return match && { protocol: match[1], host: match[2], hostname: match[3], port: match[4], pathname: match[5], search: match[6], hash: match[7] } } };