├── .gitignore ├── Procfile ├── .npmignore ├── license.txt ├── resources ├── templates │ ├── featureServiceLayers.json │ ├── services.json │ ├── queryCount.json │ ├── info.json │ ├── queryIds.json │ ├── featureService_layerItem.json │ ├── featureSet.json │ ├── drawingInfo │ │ ├── line.json │ │ ├── polygon.json │ │ └── point.json │ ├── dataProviders.html │ ├── featureService.json │ ├── featureServiceLayers.html │ ├── services.html │ ├── featureServiceLayer.json │ ├── info.html │ ├── featureServiceLayer_layerItem.html │ ├── featureServiceLayer.html │ └── featureService.html ├── favicon.ico ├── crossdomain.xml ├── clientaccesspolicy.xml ├── webmaps │ ├── jsapi │ │ ├── index.html │ │ ├── credstore.js │ │ └── main.js │ ├── esri-leaflet │ │ ├── index.html │ │ └── main.js │ └── world-bikeshares │ │ ├── index.html │ │ ├── main.js │ │ └── main.css └── ESRI.ArcGIS.SDS.REST.css ├── docs ├── structure.png ├── citibike-svc.png └── structure.graffle │ ├── data.plist │ └── image1.tiff ├── samples ├── citybikes │ ├── package.json │ └── resources │ │ ├── templates │ │ ├── layerDefinition-drawingInfo.json │ │ └── popupInfo.json │ │ ├── esribikeshare.js │ │ ├── hubway.js │ │ └── bayareabikeshare.js ├── geohub │ ├── package.json │ ├── README.md │ └── geohubprovider.js └── currentweather │ ├── package.json │ ├── utils │ ├── timer.js │ └── retriever.js │ ├── data │ └── fields.json │ └── currentwx.js ├── package.json ├── src ├── dataprovidercache.js ├── urls.js ├── query.js ├── output.js └── dataproviderbase.js ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/node-geoservices-adaptor/HEAD/license.txt -------------------------------------------------------------------------------- /resources/templates/featureServiceLayers.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers": [], 3 | "tables": [] 4 | } -------------------------------------------------------------------------------- /resources/templates/services.json: -------------------------------------------------------------------------------- 1 | { 2 | "currentVersion": 10.0, 3 | "services": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/node-geoservices-adaptor/HEAD/docs/structure.png -------------------------------------------------------------------------------- /samples/citybikes/package.json: -------------------------------------------------------------------------------- 1 | { "name" : "citybikes-dataprovider", 2 | "main" : "./citybikes.js" } -------------------------------------------------------------------------------- /docs/citibike-svc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/node-geoservices-adaptor/HEAD/docs/citibike-svc.png -------------------------------------------------------------------------------- /resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/node-geoservices-adaptor/HEAD/resources/favicon.ico -------------------------------------------------------------------------------- /resources/templates/queryCount.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers":[ 3 | { 4 | "id" : 0, 5 | "count" : 0 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /docs/structure.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/node-geoservices-adaptor/HEAD/docs/structure.graffle/data.plist -------------------------------------------------------------------------------- /docs/structure.graffle/image1.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Esri/node-geoservices-adaptor/HEAD/docs/structure.graffle/image1.tiff -------------------------------------------------------------------------------- /resources/templates/info.json: -------------------------------------------------------------------------------- 1 | { 2 | "currentVersion": 10.0, 3 | "fullVersion": "10.0", 4 | "authInfo": { 5 | "isTokenBasedSecurity": false 6 | } 7 | } -------------------------------------------------------------------------------- /resources/templates/queryIds.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers":[ 3 | { 4 | "id" : 0, 5 | "objectIdFieldName": "id", 6 | "objectIds": [] 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /samples/geohub/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geohubdataprovider", 3 | "main": "./geohubprovider.js", 4 | "version": "0.0.1", 5 | "dependencies": { 6 | "geohub": "*" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/currentweather/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "currentweather", 3 | "main": "./currentwx.js", 4 | "version": "0.0.1", 5 | "dependencies": { 6 | "terraformer-arcgis-parser": "*" 7 | } 8 | } -------------------------------------------------------------------------------- /resources/templates/featureService_layerItem.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": -1, 3 | "name": "dummyLayer", 4 | "parentLayerId": -1, 5 | "defaultVisibility": true, 6 | "subLayerIds": null, 7 | "minScale": 0, 8 | "maxScale": 0 9 | } 10 | -------------------------------------------------------------------------------- /resources/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/templates/featureSet.json: -------------------------------------------------------------------------------- 1 | { 2 | "objectIdFieldName" : "id", 3 | "globalIdFieldName" : "", 4 | "geometryType" : "esriGeometryPoint", 5 | "spatialReference" : {"wkid" : 4326}, 6 | "hasZ" : false, 7 | "hasM" : false, 8 | "fields" : [], 9 | "features" : [] 10 | } 11 | -------------------------------------------------------------------------------- /resources/clientaccesspolicy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/templates/drawingInfo/line.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": { 3 | "type": "simple", 4 | "symbol": { 5 | "type": "esriSLS", 6 | "style": "esriSLSSolid", 7 | "color": [ 8 | 32, 9 | 174, 10 | 230, 11 | 191 12 | ], 13 | "width": 1.5 14 | }, 15 | "label": "", 16 | "description": "" 17 | }, 18 | "transparency": 0, 19 | "labelingInfo": null 20 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-geoservices-adaptor", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "express": "3.x", 6 | "xml2js": ">=0.2.x", 7 | "async": "*", 8 | "geohub": "*", 9 | "terraformer": "https://github.com/nixta/Terraformer/archive/nixta-dist-node.tar.gz", 10 | "request": "~2.27.0" 11 | }, 12 | "engines": { 13 | "node": ">= 0.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /resources/templates/drawingInfo/polygon.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": { 3 | "type": "simple", 4 | "symbol": { 5 | "type": "esriSFS", 6 | "style": "esriSFSSolid", 7 | "color": [ 8 | 32, 9 | 174, 10 | 230, 11 | 191 12 | ], 13 | "outline": { 14 | "type": "esriSLS", 15 | "style": "esriSLSSolid", 16 | "color": [ 17 | 12, 18 | 79, 19 | 105, 20 | 89 21 | ], 22 | "width": 1.5 23 | } 24 | }, 25 | "label": "", 26 | "description": "" 27 | }, 28 | "transparency": 0, 29 | "labelingInfo": null 30 | } -------------------------------------------------------------------------------- /resources/webmaps/jsapi/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Esri JS API Map 9 | 10 | 11 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /resources/templates/dataProviders.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Geoservices Data Providers 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
Virtual Services - Top-level REST Endpoints
15 |
16 |

Geoservices Data Providers:

17 |
18 |
19 | DataProviders:
20 |
21 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /resources/templates/drawingInfo/point.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": { 3 | "type": "simple", 4 | "symbol": { 5 | "type": "esriPMS", 6 | "url": "blue_small_dot.png", 7 | "imageData": "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAA+UlEQVQYV42QPU7DMBiG/SvXDZSUSjAhmaE7vQkszM0NegVOwBGyV0LlJmVnwBJbECUEuanlP2qXVKJi4BtsfdbzvbYfCH5KlC85HWTX0EMRjwIK0jTqURaXdexhXMbz6opQvBj1mcgYSaNKW/C+1tIad/N8e/YEYxLPB8uLPBOM4O6CtGvrwGutZFs3Ezh+qKanvFcO++wX1DUfaw1W7abYgm/350d8dpjWgTG1atq7BI44m1GC/kw01oOV0hGsphmlZe/gfd3UZpuojCnSZ9jJ8ZITLBBMEvblQwCtdVJ/fk32ehBGC4ygQDtjwIMAnA/SO7/T81/h3/9YcxdBNn3dAAAAAElFTkSuQmCC", 8 | "contentType": "image/png", 9 | "width": 9.75, 10 | "height": 9.75, 11 | "angle": 0, 12 | "xoffset": 0, 13 | "yoffset": 0 14 | }, 15 | "label": "", 16 | "description": "" 17 | }, 18 | "transparency": 0, 19 | "labelingInfo": null 20 | } -------------------------------------------------------------------------------- /resources/templates/featureService.json: -------------------------------------------------------------------------------- 1 | { 2 | "currentVersion": 10.0, 3 | "serviceDescription": "", 4 | "hasVersionedData": false, 5 | "supportsDisconnectedEditing": false, 6 | "supportedQueryFormats": "JSON", 7 | "maxRecordCount": 2000, 8 | "hasStaticData": true, 9 | "capabilities": "Query", 10 | "description": "", 11 | "copyrightText": "", 12 | "spatialReference": { 13 | "wkid": 4326, 14 | "latestWkid": 4326 15 | }, 16 | "initialExtent": { 17 | "xmin": -180, 18 | "ymin": -90, 19 | "xmax": 180, 20 | "ymax": 90, 21 | "spatialReference": { 22 | "wkid": 4326, 23 | "latestWkid": 4326 24 | } 25 | }, 26 | "fullExtent": { 27 | "xmin": -180, 28 | "ymin": -90, 29 | "xmax": 180, 30 | "ymax": 90, 31 | "spatialReference": { 32 | "wkid": 4326, 33 | "latestWkid": 4326 34 | } 35 | }, 36 | "allowGeometryUpdates": false, 37 | "units": "esriDecimalDegrees", 38 | "syncEnabled": false, 39 | "layers": [], 40 | "tables": [] 41 | } -------------------------------------------------------------------------------- /resources/templates/featureServiceLayers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | All Layers and Tables (%s) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
ArcGIS REST Services Directory
14 | 15 | 16 | 17 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 |
27 | JSON 28 |
31 |
32 |

All Layers and Tables (%s)

33 |
34 |
35 |

Layers:
36 |

39 | 40 | 41 | -------------------------------------------------------------------------------- /resources/templates/services.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Folder: / 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
ArcGIS REST Services Directory
15 | 16 | 17 | 18 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 |
28 | JSON 29 |
32 |
33 |

Folder: /

34 |
35 |
36 | Current Version: %d
37 |
38 | Services:
39 |
40 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /resources/templates/featureServiceLayer.json: -------------------------------------------------------------------------------- 1 | { 2 | "currentVersion": 10.0, 3 | "id" : 0, 4 | "name" : "Not Set", 5 | "type" : "Feature Layer", 6 | "displayField" : "name", 7 | "description" : "", 8 | "copyrightText" : "", 9 | "defaultVisibility": true, 10 | "relationships": [], 11 | 12 | "syncCanReturnChanges": false, 13 | "isDataVersioned": false, 14 | "supportsRollbackOnFailureParameter": false, 15 | "supportsStatistics": false, 16 | "supportsAdvancedQueries":false, 17 | 18 | "geometryType" : "esriGeometryPoint", 19 | "minScale" : 0, 20 | "maxScale" : 0, 21 | "extent" : { 22 | "xmin": -180, 23 | "ymin": -90, 24 | "xmax": 180, 25 | "ymax": 90, 26 | "spatialReference": { 27 | "wkid": 4326, 28 | "latestWkid": 4326 29 | } 30 | }, 31 | 32 | 33 | "hasM": false, 34 | "hasZ": false, 35 | "allowGeometryUpdates": false, 36 | 37 | "hasAttachments" : false, 38 | 39 | "htmlPopupType" : "esriServerHTMLPopupTypeNone", 40 | 41 | "objectIdField" : "id", 42 | "globalIdField" : "", 43 | "typeIdField" : "", 44 | "fields" : [], 45 | "types" : [], 46 | "templates" : [], 47 | "maxRecordCount": 2000, 48 | "supportedQueryFormats": "JSON", 49 | "hasStaticData" : true, 50 | "capabilities" : "Query" 51 | } -------------------------------------------------------------------------------- /resources/templates/info.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Server Info 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
ArcGIS REST Services Directory
15 | 16 | 17 | 18 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 |
28 | JSON 29 |
32 |
33 |

Server Info

34 |
35 |
36 | Current Version: %d
37 |
38 | Full Version: %s
39 |
40 | Authentication Information:
41 |
42 | Owning System Url: http://www.arcgis.com
43 |
44 | 48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /resources/webmaps/jsapi/credstore.js: -------------------------------------------------------------------------------- 1 | var cred = "esri_jsapi_id_manager_data"; 2 | 3 | function loadCredentials() { 4 | var idJson, idObject; 5 | 6 | if ( supports_local_storage() ) { 7 | // read from local storage 8 | idJson = window.localStorage.getItem(cred); 9 | } else { 10 | // read from a cookie 11 | idJson = dojo.cookie(cred); 12 | } 13 | 14 | if ( idJson && idJson != "null" && idJson.length > 4) { 15 | idObject = dojo.fromJson(idJson); 16 | esri.id.initialize(idObject); 17 | } else { 18 | // console.log("didn't find anything to load :("); 19 | } 20 | } 21 | 22 | function storeCredentials() { 23 | // make sure there are some credentials to persist 24 | if ( esri.id.credentials.length === 0 ) { 25 | return; 26 | } 27 | 28 | // serialize the ID manager state to a string 29 | var idString = dojo.toJson(esri.id.toJson()); 30 | // store it client side 31 | if ( supports_local_storage() ) { 32 | // use local storage 33 | window.localStorage.setItem(cred, idString); 34 | // console.log("wrote to local storage"); 35 | } else { 36 | // use a cookie 37 | dojo.cookie(cred, idString, { expires: 1 }); 38 | // console.log("wrote a cookie :-/"); 39 | } 40 | } 41 | 42 | function supports_local_storage() { 43 | try { 44 | return "localStorage" in window && window["localStorage"] !== null; 45 | } catch( e ) { 46 | return false; 47 | } 48 | } -------------------------------------------------------------------------------- /resources/templates/featureServiceLayer_layerItem.html: -------------------------------------------------------------------------------- 1 | Name: %s
2 |
3 | Display Field: %s
4 |
5 | Type: Feature Layer
6 |
7 | Geometry Type: %s
8 |
9 | Description: %s
10 |
11 | Copyright Text: %s
12 |
13 | Min. Scale: %d
14 |
15 | Max. Scale: %d
16 |
17 | Default Visibility: true
18 |
19 | Max Record Count: %d
20 |
21 | Supported query Formats: JSON
22 |
23 | Extent:
24 | %s 25 |
26 | Drawing Info:
27 | 36 | HasZ: false
37 |
38 | HasM: false
39 |
40 | Has Attachments: false
41 |
42 | HTML Popup Type: esriServerHTMLPopupTypeNone
43 |
44 | Global ID Field:
45 |
46 | Type ID Field:
47 |
48 | Fields: 49 | 52 |
53 | Templates:
54 | Is Data Versioned: false
55 |
56 | Supports Rollback On Failure Parameter: true
57 |
-------------------------------------------------------------------------------- /resources/webmaps/esri-leaflet/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Esri-Leaflet Map 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 |
26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /resources/webmaps/world-bikeshares/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | World Bikeshares 9 | 10 | 11 | 12 | 25 | 26 | 27 |
28 |
Local Bikeshare View
29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 |
37 |
38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /samples/currentweather/utils/timer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Timer that runs in a node child process. 3 | * Can be used to control multiple things in the main application. 4 | * @type {*} 5 | */ 6 | 7 | var timers = require("timers"); 8 | 9 | var ___backgroundTimer; 10 | 11 | /** 12 | * @param msg Object 13 | */ 14 | process.on('message',function(msg){ 15 | 16 | if(msg.start == true){ 17 | 18 | var count = 0; 19 | 20 | ___backgroundTimer = timers.setInterval(function(){ 21 | try{ 22 | var date = new Date(); 23 | console.log("timer.js: datetime tick: " + date.toUTCString()); 24 | process.send("tick"); 25 | } 26 | catch(err){ 27 | count++; 28 | if(count == 3){ 29 | console.log("timer.js: shutdown timer...too many errors. " + err.message); 30 | clearInterval(___backgroundTimer); 31 | process.disconnect(); 32 | } 33 | else{ 34 | console.log("timer.js: " + err.message + "\n" + err.stack); 35 | } 36 | } 37 | },msg.interval); 38 | } 39 | }) 40 | 41 | //process.on('exit',function(){ 42 | // console.log("Shutting down timer.js"); 43 | // clearInterval(___backgroundTimer); 44 | // process.disconnect(); 45 | //}) 46 | 47 | process.on('uncaughtException',function(err){ 48 | console.log("timer.js: " + err.message + "\n" + err.stack + "\n Stopping background timer"); 49 | clearInterval(___backgroundTimer); 50 | }) -------------------------------------------------------------------------------- /resources/templates/featureServiceLayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Layer: %s (ID:%d) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
ArcGIS REST Services Directory
14 | 15 | 16 | 17 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 |
27 | JSON 28 |
31 |
32 |

Layer: %s (ID:%d)

33 |
34 |
35 | View In: 36 |   ArcGIS.com Map 37 |   Esri JS API Map 38 |   Esri-Leaflet Map 39 |   ArcGIS Explorer Online 40 |

41 | %s 42 | Supported Operations: 43 |   Query 44 |

45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /src/dataprovidercache.js: -------------------------------------------------------------------------------- 1 | var rtree = require("terraformer/RTree"), 2 | geostore = require("terraformer/GeoStore"), 3 | memoryStore = require("terraformer/Stores/Memory"); 4 | 5 | DataProviderCache = function(serviceId, layerId, cacheLifetimeInSeconds) { 6 | this.cacheLifetime = ((arguments >= 3)?cacheLifetimeInSeconds:60*60) * 1000; 7 | this.serviceId = serviceId; 8 | this.layerId = layerId; 9 | this.cacheId = serviceId + "_" + layerId; 10 | 11 | this.expirationUTC = null; 12 | this.store = null; 13 | this._status = null; 14 | this.resetCache(); 15 | 16 | this.layerDetails = {}; 17 | } 18 | 19 | DataProviderCache.prototype = { 20 | get isExpired() { 21 | return new Date().getTime() >= this.expirationUTC; 22 | }, 23 | get status() { 24 | return this._status; 25 | }, 26 | set status(newStatus) { 27 | var oldStatus = this._status; 28 | this._status = newStatus; 29 | if (newStatus === "loaded") { 30 | this.extendCache(); 31 | } 32 | }, 33 | canExtendCache: function() { 34 | return true; 35 | }, 36 | extendCache: function() { 37 | if (this.canExtendCache()) { 38 | this.expirationUTC = new Date().getTime() + this.cacheLifetime; 39 | return true; 40 | } else { 41 | return false; 42 | } 43 | }, 44 | validateCache: function() { 45 | if (!this.isExpired) { 46 | return true; 47 | } else { 48 | return this.extendCache(); 49 | } 50 | }, 51 | resetCache: function() { 52 | this.expirationUTC = new Date(); 53 | 54 | this.store = new geostore.GeoStore({ 55 | store: new memoryStore.Memory(), 56 | index: new rtree.RTree() 57 | }); 58 | 59 | this._status = "waitingToLoad"; 60 | } 61 | } 62 | 63 | exports.DataProviderCache = DataProviderCache; 64 | -------------------------------------------------------------------------------- /samples/citybikes/resources/templates/layerDefinition-drawingInfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderer": { 3 | "type": "uniqueValue", 4 | "field1": "bikesClass", 5 | "defaultSymbol": null, 6 | "uniqueValueInfos": [{ 7 | "value": "Plenty of bikes", 8 | "symbol": { 9 | "color": [50, 205, 50, 255], 10 | "size": 9, 11 | "angle": 0, 12 | "xoffset": 0, 13 | "yoffset": 0, 14 | "type": "esriSMS", 15 | "style": "esriSMSCircle", 16 | "outline": { 17 | "color": [0, 0, 128, 255], 18 | "width": 0, 19 | "type": "esriSLS", 20 | "style": "esriSLSSolid" 21 | } 22 | }, 23 | "label": "Plenty of bikes" 24 | }, { 25 | "value": "A few bikes", 26 | "symbol": { 27 | "color": [255, 215, 0, 255], 28 | "size": 7.5, 29 | "angle": 0, 30 | "xoffset": 0, 31 | "yoffset": 0, 32 | "type": "esriSMS", 33 | "style": "esriSMSCircle", 34 | "outline": { 35 | "color": [0, 0, 128, 255], 36 | "width": 0, 37 | "type": "esriSLS", 38 | "style": "esriSLSSolid" 39 | } 40 | }, 41 | "label": "A few bikes" 42 | }, { 43 | "value": "1 bike", 44 | "symbol": { 45 | "color": [255, 127, 80, 255], 46 | "size": 5.25, 47 | "angle": 0, 48 | "xoffset": 0, 49 | "yoffset": 0, 50 | "type": "esriSMS", 51 | "style": "esriSMSCircle", 52 | "outline": { 53 | "color": [0, 0, 128, 255], 54 | "width": 0, 55 | "type": "esriSLS", 56 | "style": "esriSLSSolid" 57 | } 58 | }, 59 | "label": "1 bike" 60 | }, { 61 | "value": "No bikes", 62 | "symbol": { 63 | "color": [0, 0, 0, 0], 64 | "size": 3, 65 | "angle": 0, 66 | "xoffset": 0, 67 | "yoffset": 0, 68 | "type": "esriSMS", 69 | "style": "esriSMSX", 70 | "outline": { 71 | "color": [240, 128, 128, 128], 72 | "width": 1, 73 | "type": "esriSLS", 74 | "style": "esriSLSSolid" 75 | } 76 | }, 77 | "label": "No bikes" 78 | }] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /resources/templates/featureService.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | %s (%s) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
ArcGIS REST Services Directory
15 | 16 | 17 | 18 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 |
28 | JSON 29 |
32 |
33 |

%s (%s)

34 |
35 |
36 | View In: (Not Yet Implemented) 39 |

40 | Service Description: %s
41 |
42 | Has Versioned Data: %s
43 |
44 | Max Record Count: %d
45 |
46 | Supported query Formats: %s
47 |
48 | All Layers and Tables
49 |
50 | Layers:
51 |
54 | Description: %s
55 |
56 | Copyright Text: %s
57 |
58 | Spatial Reference: %s
59 |
60 | Initial Extent:
61 | %s 62 | Full Extent:
63 | %s 64 | Units: %s
65 |
66 | Supported Operations: 67 |   None 68 |

69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /src/urls.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | // We will support URLs of the format /rest//FeatureServer/0/query 4 | // etc. but the important thing to note is that is effectively a virtual 5 | // ArcGIS Server root which maps directly to a data provider module inheriting from dataproviderbase 6 | var serviceBaseUrl = '/%s/rest'; 7 | var servicesUrl = serviceBaseUrl + '/services'; 8 | var infoUrl = serviceBaseUrl + '/info'; 9 | var serviceUrlTemplate = servicesUrl + '/%s/FeatureServer'; 10 | var layersUrlTemplate = serviceUrlTemplate + '/layers'; 11 | var layerUrlTemplate = serviceUrlTemplate + '/%s'; 12 | var queryUrlTemplate = layerUrlTemplate + '/query%s'; 13 | 14 | var queryCatchAllTemplate = '(?:/[^?]+)?'; 15 | 16 | Urls = function(dataProvider) { 17 | dataProvider = dataProvider || { 18 | name: ":dataProviderName" 19 | }; 20 | this.dataProvider = dataProvider; 21 | }; 22 | 23 | Urls.prototype = { 24 | getServicesUrl: function() { 25 | return util.format(servicesUrl, this.dataProvider.name); 26 | }, 27 | 28 | getInfoUrl: function() { 29 | return util.format(infoUrl, this.dataProvider.name); 30 | }, 31 | 32 | getServiceUrl: function(serviceId) { 33 | if (arguments.length < 1) serviceId = ":serviceId"; 34 | return util.format(serviceUrlTemplate, this.dataProvider.name, serviceId); 35 | }, 36 | 37 | getLayersUrl: function(serviceId) { 38 | if (arguments.length < 1) serviceId = ":serviceId"; 39 | return util.format(layersUrlTemplate, this.dataProvider.name, serviceId); 40 | }, 41 | 42 | getLayerUrl: function(serviceId, layerId) { 43 | if (arguments.length < 2) layerId = ":layerId"; 44 | if (arguments.length < 1) serviceId = ":serviceId"; 45 | return util.format(layerUrlTemplate, this.dataProvider.name, serviceId, layerId); 46 | }, 47 | 48 | getLayerQueryUrl: function(serviceId, layerId) { 49 | var q = ''; 50 | if (arguments.length < 2) { 51 | layerId = ":layerId"; 52 | q = queryCatchAllTemplate; 53 | } 54 | if (arguments.length < 1) serviceId = ":serviceId"; 55 | return util.format(queryUrlTemplate, this.dataProvider.name, serviceId, layerId, q); 56 | } 57 | } 58 | 59 | exports.Urls = Urls; 60 | -------------------------------------------------------------------------------- /resources/webmaps/jsapi/main.js: -------------------------------------------------------------------------------- 1 | var map = null; 2 | 3 | var url = getParameterByName("url"), 4 | options = { 5 | basemap: "gray", 6 | sliderStyle: "small" 7 | }; 8 | 9 | function getParameterByName(name) { 10 | name = name.replace(/[\[]/, "\[").replace(/[\]]/, "\]"); 11 | var regex = new RegExp("[\?&]" + name + "=([^&#]*)"), 12 | results = regex.exec(location.search); 13 | return results == null ? null : decodeURIComponent(results[1].replace(/\+/g, " ")); 14 | } 15 | 16 | function getExtent(layerEndpoint, callback) { 17 | var featureServiceDescriptionUrl = layerEndpoint + "?f=json"; 18 | var jsonfile = new XMLHttpRequest(); 19 | jsonfile.open("GET", featureServiceDescriptionUrl, true); 20 | jsonfile.onreadystatechange = function() { 21 | if (jsonfile.readyState == 4) { 22 | if (jsonfile.status == 200) { 23 | require(["esri/geometry/Extent"], function(Extent) { 24 | var extent = JSON.parse(jsonfile.responseText).extent; 25 | extent = new Extent(extent); 26 | return callback(null, extent); 27 | }); 28 | } else { 29 | return callback("Could not get extent", null); 30 | } 31 | } 32 | }; 33 | jsonfile.send(null); 34 | } 35 | 36 | function initMapWithFeatureLayerExtent(endpoint, mapOptions) { 37 | getExtent(endpoint, function(err, extent) { 38 | if (err) { 39 | alert(err); 40 | return; 41 | } 42 | require(["esri/map", "esri/layers/FeatureLayer", 43 | "esri/dijit/InfoWindowLite", "esri/InfoTemplate", 44 | "dojo/dom-construct", "dojo/domReady!"], 45 | function(Map, FeatureLayer, InfoWindowLite, InfoTemplate, domConstruct) { 46 | map = new Map("map", mapOptions); 47 | 48 | var infoWindow = new InfoWindowLite(null, domConstruct.create("div", null, null, map.root)); 49 | infoWindow.startup(); 50 | map.setInfoWindow(infoWindow); 51 | 52 | map.on("load", function() { 53 | map.setExtent(extent, true); 54 | var template = new InfoTemplate(); 55 | var featureLayer = new FeatureLayer(endpoint, { 56 | infoTemplate: template 57 | }); 58 | map.addLayer(featureLayer); 59 | }); 60 | }); 61 | }); 62 | } 63 | 64 | initMapWithFeatureLayerExtent(url, options); 65 | -------------------------------------------------------------------------------- /samples/currentweather/data/fields.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"name" : "id", "type" : "esriFieldTypeString", "alias" : "Station ID","length" : "255", "nullable" : "true"}, 3 | {"name" : "locationText", "type" : "esriFieldTypeString", "alias" : "Location", "nullable" : "true"}, 4 | {"name" : "pickupTimeMinutes", "type" : "esriFieldTypeInteger", "alias" : "Pickup Time Mins", "nullable" : "false"}, 5 | {"name" : "latitude", "type" : "esriFieldTypeDouble", "alias" : "Latitude", "nullable" : "true"}, 6 | {"name" : "longitude", "type" : "esriFieldTypeDouble", "alias" : "Longitude", "nullable" : "true"}, 7 | {"name" : "time", "type" : "esriFieldTypeDate", "alias" : "Time", "nullable" : "true"}, 8 | {"name" : "weather", "type" : "esriFieldTypeString", "alias" : "Weather","length" : "255", "nullable" : "true"}, 9 | {"name" : "tempF", "type" : "esriFieldTypeDouble", "alias" : "Temperature Farenheit", "nullable" : "true"}, 10 | {"name" : "tempC", "type" : "esriFieldTypeDouble", "alias" : "Temperature Celcius", "nullable" : "true"}, 11 | {"name" : "relativeHumidity", "type" : "esriFieldTypeDouble", "alias" : "Relative Humidity", "nullable" : "true"}, 12 | {"name" : "windDirection", "type" : "esriFieldTypeString", "alias" : "Wind Direction","length" : "255", "nullable" : "true"}, 13 | {"name" : "windDegrees", "type" : "esriFieldTypeInteger", "alias" : "Wind Degrees", "nullable" : "true"}, 14 | {"name" : "windMPH", "type" : "esriFieldTypeDouble", "alias" : "Wind MPH", "nullable" : "true"}, 15 | {"name" : "windKnots", "type" : "esriFieldTypeDouble", "alias" : "Wind Knots", "nullable" : "true"}, 16 | {"name" : "pressureMb", "type" : "esriFieldTypeDouble", "alias" : "Pressure Millibars", "nullable" : "true"}, 17 | {"name" : "pressureIn", "type" : "esriFieldTypeDouble", "alias" : "Pressure Inches", "nullable" : "true"}, 18 | {"name" : "dewPointF", "type" : "esriFieldTypeDouble", "alias" : "Dewpoint Farenheit", "nullable" : "true"}, 19 | {"name" : "dewPointC", "type" : "esriFieldTypeDouble", "alias" : "Dewpoint Celcius", "nullable" : "true"}, 20 | {"name" : "heatIndexF", "type" : "esriFieldTypeDouble", "alias" : "Heat Index Farenheit", "nullable" : "true"}, 21 | {"name" : "heatIndexC", "type" : "esriFieldTypeDouble", "alias" : "Heat Index Celcius", "nullable" : "true"}, 22 | {"name" : "visibilityMi", "type" : "esriFieldTypeDouble", "alias" : "Visbility Miles", "nullable" : "true"}, 23 | {"name" : "twoDayHistoryURL", "type" : "esriFieldTypeString", "alias" : "Two Day History URL","length" : "255", "nullable" : "true"} 24 | ] -------------------------------------------------------------------------------- /resources/webmaps/esri-leaflet/main.js: -------------------------------------------------------------------------------- 1 | var url = getParameterByName("url"), 2 | layerFS = null; 3 | 4 | function getParameterByName(name) { 5 | name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); 6 | var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), 7 | results = regex.exec(location.search); 8 | return results == null ? null : decodeURIComponent(results[1].replace(/\+/g, " ")); 9 | } 10 | 11 | function setExtentToFeatureLayer(endpoint) { 12 | var featureServiceDescriptionUrl = endpoint + "?f=json"; 13 | var jsonfile = new XMLHttpRequest(); 14 | jsonfile.open("GET", featureServiceDescriptionUrl, true); 15 | jsonfile.onreadystatechange = function() { 16 | if (jsonfile.readyState == 4) { 17 | if (jsonfile.status == 200) { 18 | var extent = JSON.parse(jsonfile.responseText).extent; 19 | console.log(extent); 20 | var bounds = { 21 | "type": "MultiPoint", 22 | "coordinates": [ 23 | [extent.ymin, extent.xmin], 24 | [extent.ymax, extent.xmax] 25 | ] 26 | }; 27 | if (extent.spatialReference.wkid === 102100) { 28 | var bc = bounds.coordinates; 29 | bounds.coordinates = [ 30 | [bc[0][1], bc[0][0]], 31 | [bc[1][1], bc[1][0]] 32 | ]; 33 | bounds = Terraformer.toGeographic(bounds); 34 | bc = bounds.coordinates; 35 | bounds.coordinates = [ 36 | [bc[0][1], bc[0][0]], 37 | [bc[1][1], bc[1][0]] 38 | ]; 39 | } 40 | map.fitBounds(bounds.coordinates); 41 | } 42 | } 43 | }; 44 | jsonfile.send(null); 45 | } 46 | 47 | function addFeatureLayer(featureServiceUrl) { 48 | // Add ArcGIS Online feature service 49 | setExtentToFeatureLayer(featureServiceUrl); 50 | L.esri.featureLayer(featureServiceUrl, { 51 | onEachFeature: createPopup 52 | }).addTo(map); 53 | } 54 | 55 | function createPopup(geojson, layer) { 56 | // Show all data 57 | if (geojson.properties) { 58 | var popupText = "
"; 59 | for (prop in geojson.properties) { 60 | var val = geojson.properties[prop]; 61 | if (val) { 62 | popupText += "" + prop + ": " + val + "
"; 63 | } 64 | } 65 | popupText += "
"; 66 | layer.bindPopup(popupText); 67 | } 68 | } 69 | 70 | if (url) { 71 | addFeatureLayer(url); 72 | } 73 | -------------------------------------------------------------------------------- /samples/citybikes/resources/esribikeshare.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); 2 | 3 | var networkTemplate = { 4 | "city": "Redlands", 5 | "name": "esribike", 6 | "url": "http://www.esri.com", 7 | "radius": 20000, 8 | "lat": 34056490, 9 | "lng": -117195681, 10 | "id": 10000 11 | }, 12 | stationTemplate = { 13 | "name": "Esri Campus", 14 | "idx": 0, 15 | "timestamp": "2013-08-28T19:07:09.919602", 16 | "number": 1, 17 | "free": 4, 18 | "bikes": 4, 19 | "coordinates": "", 20 | "address": "380 New York Street", 21 | "lat": 34056490, 22 | "lng": -117195681, 23 | "id": 0 24 | }, 25 | maxBikes = 4, 26 | updateDuration = 30000; // milliseconds 27 | 28 | var bikeClassificationScheme = { 29 | "0": { "min": 0, "max": 0, "label": "No bikes" }, 30 | "1": { "min": 1, "max": 1, "label": "1 bike" }, 31 | "few": { "min": 2, "max": 2, "label": "A few bikes" }, 32 | "plenty": { "min": 3, "max": 10000, "label": "Plenty of bikes" } 33 | }; 34 | 35 | var dockClassificationScheme = { 36 | "0": { "min": 0, "max": 0, "label": "No docks" }, 37 | "1": { "min": 1, "max": 1, "label": "1 dock" }, 38 | "2": { "min": 2, "max": 2, "label": "2 docks" }, 39 | "3": { "min": 3, "max": 3, "label": "3 docks" }, 40 | "plenty": { "min": 4, "max": 10000, "label": "Plenty of docks" } 41 | }; 42 | 43 | function zeroPad(number) { 44 | return ("0000" + number).slice(-2) 45 | } 46 | 47 | function getNewStationStatus() { 48 | var station = JSON.parse(JSON.stringify(stationTemplate)); 49 | 50 | var d = new Date(); 51 | d.setTime(d.getTime() + 2 * 60 * 60 * 1000); 52 | station.timestamp = util.format("%s-%s-%sT%s:%s:%s.%s", d.getUTCFullYear(), 53 | zeroPad(d.getUTCMonth()), zeroPad(d.getUTCDate()), zeroPad(d.getUTCHours()), 54 | zeroPad(d.getUTCMinutes()), zeroPad(d.getUTCSeconds()), d.getUTCMilliseconds()); 55 | 56 | station.bikes = Math.floor(Math.random() * (maxBikes + 1)); 57 | station.free = maxBikes - station.bikes; 58 | 59 | return station; 60 | } 61 | 62 | EsriBikeshare = function() { 63 | this.network = JSON.parse(JSON.stringify(networkTemplate)); 64 | this.name = this.network.name; 65 | this._stations = []; 66 | this.nextUpdateTime = new Date(); 67 | } 68 | 69 | EsriBikeshare.prototype = { 70 | get stations() { 71 | if (this._stations.length == 0 || this.nextUpdateTime <= new Date()) { 72 | var s = getNewStationStatus(); 73 | this.nextUpdateTime = new Date() + updateDuration; 74 | this._stations = [s]; 75 | } 76 | return this._stations; 77 | }, 78 | _getBikeRange: function(provider, station) { 79 | var bikesAvailable = station.attributes.bikes; 80 | 81 | station.attributes["bikesClass"] = provider._getClassValue(bikeClassificationScheme, bikesAvailable); 82 | }, 83 | _getDockRange: function(provider, station) { 84 | var docksFree = station.attributes.free; 85 | 86 | station.attributes["docksClass"] = provider._getClassValue(dockClassificationScheme, docksFree); 87 | } 88 | 89 | } 90 | 91 | exports.EsriBikeshare = EsriBikeshare; 92 | -------------------------------------------------------------------------------- /resources/ESRI.ArcGIS.SDS.REST.css: -------------------------------------------------------------------------------- 1 | BODY { 2 | PADDING-LEFT: 22px; PADDING-RIGHT: 22px; BACKGROUND: #fff; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px; FONT-FAMILY: Verdana, Arial, Helvetica, sans-serif; min-width: 780px; 3 | } 4 | HTML { 5 | PADDING-LEFT: 0px; PADDING-RIGHT: 0px; BACKGROUND: #fff; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px; FONT-FAMILY: Verdana, Arial, Helvetica, sans-serif; min-width: 780px; 6 | } 7 | CODE { 8 | FONT-SIZE: 1.3em; FONT-FAMILY: serif 9 | } 10 | 11 | TD { 12 | PADDING-RIGHT: 11px; PADDING-LEFT: 0px; FONT-SIZE: 0.90em; PADDING-BOTTOM: 5px; MARGIN: 0px 0px 3px; PADDING-TOP: 3px; 13 | } 14 | H2 { 15 | MARGIN-LEFT: 0px; FONT-WEIGHT: bold; FONT-SIZE: 1.2em 16 | } 17 | H3 { 18 | FONT-WEIGHT: bold; FONT-SIZE: 1.25em; MARGIN-BOTTOM: 0px; 19 | } 20 | LI { 21 | PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 3px; PADDING-TOP: 0px; 22 | } 23 | .restBody 24 | { 25 | FONT-SIZE:0.9em; MARGIN-LEFT: 0px; 26 | } 27 | .apiref { 28 | FONT-SIZE: 0.6em; MARGIN: 0px; PADDING: 0px; 29 | } 30 | .titlecell { 31 | PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-SIZE: 1.0em; FONT-WEIGHT:bold; PADDING-BOTTOM: 5px; MARGIN: 0px 0px 3px; PADDING-TOP: 3px; 32 | } 33 | .breadcrumbs { 34 | PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-SIZE: 0.8em; FONT-WEIGHT: bold; PADDING-BOTTOM: 5px; MARGIN: 0px 0px 3px; PADDING-TOP: 0px; 35 | } 36 | .userTable { 37 | FONT-SIZE: 0.80em; 38 | } 39 | .navTable { 40 | PADDING-BOTTOM: 5px; MARGIN: 0px 0px 3px; PADDING-TOP: 0px; BORDER-BOTTOM: #000 1px solid; BORDER-TOP: #000 1px solid; BACKGROUND-COLOR: #E5EFF7; 41 | } 42 | .adminNavTable { 43 | BORDER-BOTTOM: #000 1px solid; PADDING-BOTTOM: 5px; BACKGROUND-COLOR: #f7efe5; MARGIN: 0px 0px 3px; BORDER-TOP: #000 1px solid; PADDING-TOP: 0px 44 | } 45 | .help { 46 | PADDING-RIGHT: 11px; PADDING-LEFT: 0px; FONT-SIZE: 0.70em; PADDING-BOTTOM: 5px; MARGIN: 0px 0px 3px; PADDING-TOP: 3px; 47 | } 48 | .formTable { 49 | PADDING: 5px; MARGIN: 10px 0px 3px; BORDER: #000 1px solid; BACKGROUND-COLOR: #E5EFF7; 50 | } 51 | TABLE.ftrTable TR { 52 | vertical-align: top; 53 | } 54 | TABLE.ftrTable TD { 55 | padding: 2px; 56 | } 57 | 58 | .odd { 59 | background-color:#CCFFFF; 60 | } 61 | 62 | .restErrors 63 | { 64 | color: #ffaaaa; 65 | } 66 | 67 | .restException 68 | { 69 | color: red; 70 | } 71 | 72 | /* JOB SERVER ENTRIES*/ 73 | 74 | .cs_SUBMITTED { 75 | color: #00FFFF; 76 | font-weight:bold; 77 | } 78 | 79 | .cs_PROCESSING,.cs_ORANGE { 80 | color: #0000FF; 81 | font-weight:bold; 82 | } 83 | 84 | .cs_DONE,.cs_STARTED,.cs_COMPLETE,.cs_GREEN { 85 | color: #387C44; 86 | font-weight:bold; 87 | } 88 | 89 | .cs_PARTIALERROR,.cs_PARTIAL { 90 | color: #F99999; 91 | font-weight:bold; 92 | } 93 | 94 | .cs_CANCELED { 95 | color: #6B6B6B; 96 | font-weight:bold; 97 | } 98 | 99 | .cs_ERROR,.cs_STOPPED,.cs_NONE,.cs_RED { 100 | color: #FF0000; 101 | font-weight:bold; 102 | } -------------------------------------------------------------------------------- /samples/citybikes/resources/templates/popupInfo.json: -------------------------------------------------------------------------------- 1 | "popupInfo": { 2 | "title": "{name}", 3 | "fieldInfos": [{ 4 | "fieldName": "id", 5 | "label": "ID", 6 | "isEditable": true, 7 | "tooltip": "", 8 | "visible": true, 9 | "format": { 10 | "places": 0, 11 | "digitSeparator": false 12 | }, 13 | "stringFieldOption": "textbox" 14 | }, { 15 | "fieldName": "idx", 16 | "label": "IDX", 17 | "isEditable": true, 18 | "tooltip": "", 19 | "visible": true, 20 | "format": { 21 | "places": 0, 22 | "digitSeparator": false 23 | }, 24 | "stringFieldOption": "textbox" 25 | }, { 26 | "fieldName": "name", 27 | "label": "Name", 28 | "isEditable": true, 29 | "tooltip": "", 30 | "visible": true, 31 | "stringFieldOption": "textbox" 32 | }, { 33 | "fieldName": "number", 34 | "label": "Number", 35 | "isEditable": true, 36 | "tooltip": "", 37 | "visible": true, 38 | "format": { 39 | "places": 0, 40 | "digitSeparator": false 41 | }, 42 | "stringFieldOption": "textbox" 43 | }, { 44 | "fieldName": "free", 45 | "label": "Open Docks", 46 | "isEditable": true, 47 | "tooltip": "", 48 | "visible": true, 49 | "format": { 50 | "places": 0, 51 | "digitSeparator": false 52 | }, 53 | "stringFieldOption": "textbox" 54 | }, { 55 | "fieldName": "bikes", 56 | "label": "Bikes", 57 | "isEditable": true, 58 | "tooltip": "", 59 | "visible": true, 60 | "format": { 61 | "places": 0, 62 | "digitSeparator": false 63 | }, 64 | "stringFieldOption": "textbox" 65 | }, { 66 | "fieldName": "bikesClass", 67 | "label": "Bikes Class", 68 | "isEditable": true, 69 | "tooltip": "", 70 | "visible": true, 71 | "stringFieldOption": "textbox" 72 | }, { 73 | "fieldName": "docksClass", 74 | "label": "Docks Class", 75 | "isEditable": true, 76 | "tooltip": "", 77 | "visible": true, 78 | "stringFieldOption": "textbox" 79 | }, { 80 | "fieldName": "address", 81 | "label": "Address", 82 | "isEditable": true, 83 | "tooltip": "", 84 | "visible": true, 85 | "stringFieldOption": "textbox" 86 | }, { 87 | "fieldName": "citybikesTimeString", 88 | "label": "CityBikes Time", 89 | "isEditable": true, 90 | "tooltip": "", 91 | "visible": true, 92 | "stringFieldOption": "textbox" 93 | }, { 94 | "fieldName": "utcTime", 95 | "label": "UTC Timestamp", 96 | "isEditable": true, 97 | "tooltip": "", 98 | "visible": true, 99 | "format": { 100 | "dateFormat": "shortDateShortTime" 101 | }, 102 | "stringFieldOption": "textbox" 103 | }, { 104 | "fieldName": "timezone", 105 | "label": "Timezone Code", 106 | "isEditable": true, 107 | "tooltip": "", 108 | "visible": true, 109 | "stringFieldOption": "textbox" 110 | }, { 111 | "fieldName": "timezoneOffset", 112 | "label": "Timezone Offset", 113 | "isEditable": true, 114 | "tooltip": "", 115 | "visible": true, 116 | "format": { 117 | "places": 0, 118 | "digitSeparator": true 119 | }, 120 | "stringFieldOption": "textbox" 121 | }, { 122 | "fieldName": "timezoneOffsetString", 123 | "label": "Timezone Offset String", 124 | "isEditable": true, 125 | "tooltip": "", 126 | "visible": true, 127 | "stringFieldOption": "textbox" 128 | }, { 129 | "fieldName": "localTimeString", 130 | "label": "Local Time", 131 | "isEditable": true, 132 | "tooltip": "", 133 | "visible": true, 134 | "stringFieldOption": "textbox" 135 | }], 136 | "description": "There were {bikes} bikes available and {free} available docks when last checked at {utcTime}.\n ", 137 | "showAttachments": true, 138 | "mediaInfos": [{ 139 | "title": null, 140 | "type": "piechart", 141 | "caption": "{bikes} bikes\n{free} open docks", 142 | "value": { 143 | "fields": ["free", "bikes"], 144 | "normalizeField": null 145 | } 146 | }] 147 | } 148 | -------------------------------------------------------------------------------- /samples/geohub/README.md: -------------------------------------------------------------------------------- 1 | GitHub geoJSON Data Provider Sample 2 | =================================== 3 | 4 | This is a sample Data Provider for the [node-geoservices-adaptor](../..). 5 | 6 | It provides access to geoJSON files stored in GitHub by providing a wrapper around [GeoHub](https://github.com/chelm/geohub). 7 | 8 | ## GitHub Repository 9 | To reference a geoJSON file from a GitHub repository, use the following REST URL structure: 10 | 11 | /geohub/rest/services/repo+++/FeatureServer/0 12 | 13 | Where: 14 | 15 | * `` is a GitHub username. 16 | * `` is the name of a repo owned by the specified GitHub User. 17 | * `` is the name (without the `.geojson` extension) of a geoJSON file within the specified repo. It should be URL Encoded. It may include a relative path within the repository. 18 | 19 | Below is a sample URL that you can add directly to an ArcGIS Online map or consume with one of the ArcGIS APIs: 20 | 21 | * Access `forks.geojson` in chelm's grunt-geo repo: http://geonode.geeknixta.com/geohub/rest/services/repo+chelm+grunt-geo+forks/FeatureServer/0 22 | * Access `samples/bower.geojson` in chelm's grunt-geo repo: http://geonode.geeknixta.com/geohub/rest/services/repo+chelm+grunt-geo+samples%2Fbower/FeatureServer/0 23 | 24 | ## GitHub Gist 25 | 26 | To reference a geoJSON file from a GitHub Gist, use the following REST URL structure: 27 | 28 | /geohub/rest/services/gist+/FeatureServer/ 29 | 30 | Where: 31 | 32 | * `` is the ID of the gist, which is included in the Gist's URL 33 | * `` is a zero-based reference to a geoJSON file in the Gist. Note that non-geoJSON files are ignored. That is, if there are 5 files in the gist, but only 2 geoJSON files, valid values for `` are 0 and 1. 34 | 35 | Below are some sample URLs that you can add directly to an ArcGIS Online map or consume with one of the ArcGIS APIs: 36 | 37 | * Access the first geoJSON file in gist `6178185`: http://geonode.geeknixta.com/geohub/rest/services/gist+6178185/FeatureServer/0 38 | * Access the second geoJSON file in gist `6178185`: http://geonode.geeknixta.com/geohub/rest/services/gist+6178185/FeatureServer/1 39 | 40 | ## Notes 41 | ### geoJSON containing multiple geometry types 42 | 43 | An ArcGIS Feature Layer can only provide a single type of Esri Geometry. However, geoJSON files may contain any combination of [Geometry Types](http://www.geojson.org/geojson-spec.html#geometry-objects). The GeoHub Data Provider defaults to outputting only geometries matching the type of the first geometry encountered in the geoJSON file. In the case where the geoJSON includes multiple Geometry Types and the first geometry in the geoJSON file is not the type you want to provide, add the optional `+` specifier like this: 44 | 45 | **Repository**: 46 | 47 | /geohub/rest/services/repo++++/FeatureServer/0 48 | 49 | **Gist**: 50 | 51 | /geohub/rest/services/gist++/FeatureServer/ 52 | 53 | Where `` is a valid geoJSON Geometry Type as specified by the [geoJSON Specification](http://www.geojson.org/geojson-spec.html). 54 | 55 | **Please note:** 56 | 57 | * As per the specification, **Geometry Types are case-sensitive**. 58 | * `GeometryCollection` is **not supported**. 59 | 60 | Below are some sample URLs that you can add directly to an ArcGIS Online map or consume with one of the ArcGIS APIs: 61 | 62 | ##### Repository: 63 | * Access `forks.geojson` in chelm's grunt-geo repo, filtering by geometry type `LineString`: http://geonode.geeknixta.com/geohub/rest/services/repo+chelm+grunt-geo+forks+LineString/FeatureServer/0 64 | * Access `samples/bower.geojson` in chelm's grunt-geo repo, filtering by geometry type `LineString`: http://geonode.geeknixta.com/geohub/rest/services/repo+chelm+grunt-geo+samples%2Fbower+LineString/FeatureServer/0 65 | 66 | ##### Gist: 67 | * Access the second geoJSON file in gist `6178185` and view only `Point` geometries: http://geonode.geeknixta.com/geohub/rest/services/gist+6178185+Point/FeatureServer/1 68 | 69 | ### GeoHub 70 | This adaptor makes use of [GeoHub](https://github.com/chelm/geohub). -------------------------------------------------------------------------------- /samples/citybikes/resources/hubway.js: -------------------------------------------------------------------------------- 1 | var util = require("util"), 2 | request = require("request"), 3 | xml2js = require("xml2js"), 4 | parser = new xml2js.Parser(); 5 | 6 | // Hubway data is in XML and looks like this: 7 | // 8 | // 58 9 | // The Esplanade - Beacon St. at Arlington St. 10 | // D32017 11 | // 1379796612852 12 | // 42.355596 13 | // -71.07278 14 | // true 15 | // false 16 | // 1364310000000 17 | // 18 | // false 19 | // true 20 | // 8 21 | // 11 22 | // 1379796364076 23 | // 24 | 25 | var networkTemplate = { 26 | "city": "Boston", 27 | "name": "hubway", 28 | "url": "http://www.thehubway.com/data/stations/bikeStations.xml", 29 | "radius": 20000, 30 | "lat": 42355596, 31 | "lng": -71072780, 32 | "id": 10001 33 | }, 34 | stationTemplate = { 35 | "name": "Somewhere in Boston", 36 | "idx": 0, 37 | "timestamp": "2013-08-28T19:07:09.919602", 38 | "number": 1, 39 | "free": 4, 40 | "bikes": 4, 41 | "coordinates": "", 42 | "address": "Somewhere in Boston", 43 | "lat": 42355596, 44 | "lng": -71072780, 45 | "id": 0 46 | }, 47 | mapping = { 48 | "name": "name", 49 | "idx": "id", 50 | "timestamp": "latestUpdateTime", 51 | "number": "id", 52 | "free": "nbEmptyDocks", 53 | "bikes": "nbBikes", 54 | "address": "name", 55 | "lat": "lat", 56 | "lng": "long", 57 | "id": "id" 58 | }; 59 | 60 | function zeroPad(number) { 61 | return ("0000" + number).slice(-2) 62 | } 63 | 64 | HubwayBikeshare = function() { 65 | this.network = JSON.parse(JSON.stringify(networkTemplate)); 66 | this.name = this.network.name; 67 | this._stations = []; 68 | this.nextUpdateTime = new Date(); 69 | } 70 | 71 | HubwayBikeshare.prototype = { 72 | getStations: function(callback) { 73 | request(this.network.url, (function (error, response, stationsXML) { 74 | if (!error && response.statusCode == 200) { 75 | // Done eating the stations HTTP response for a given network. 76 | parser.parseString(stationsXML, (function(err, result) { 77 | if (err) { 78 | return callback(err, null); 79 | } 80 | 81 | var stations = result.stations.station; 82 | var processedStations = []; 83 | for (var i=0; i 0){ 197 | 198 | var timer = setInterval(function(){ 199 | 200 | console.log("t= " + t + ", token= " + token + ", " + totalTokens) 201 | if(t == token && count <= totalTokens){ 202 | count++; 203 | 204 | if(t <= length)t+=10; 205 | remaining = length - t; 206 | 207 | if(remaining > 10){ 208 | segment = arr.slice(previousVal,t); 209 | console.log("segment length " + segment.length) 210 | previousVal = t; 211 | } 212 | else{ 213 | segment = arr.slice(previousVal,length); 214 | } 215 | 216 | console.log("remaining = " + remaining); 217 | 218 | if(segment != null && t != 0)this._async(segment,t); 219 | if(remaining <10)clearTimeout(timer); 220 | } 221 | 222 | console.log("tick"); 223 | }.bind(this),1000); 224 | } 225 | 226 | } 227 | 228 | this._isEven= function(value){ 229 | if(value%2 == 0) 230 | return true; 231 | else 232 | return false; 233 | } 234 | 235 | this._init = function(){ 236 | 237 | this._createWxStationsArr(msg.content); 238 | this._loopStationsArr(this._wxStationsArr); 239 | //this._async(this._wxStationsArr); 240 | }.bind(this)() 241 | 242 | }) 243 | 244 | process.on('uncaughtException',function(err){ 245 | console.log("retriever.js uncaught exception: " + err.message + "\n" + err.stack); 246 | }) -------------------------------------------------------------------------------- /resources/webmaps/world-bikeshares/main.js: -------------------------------------------------------------------------------- 1 | var map = null, 2 | legend = null, 3 | worldLayerName = "world", 4 | bikeshareLayerName = "local", 5 | urlRoot = location.protocol + "//" + location.host, 6 | worldLayerURL = "/citybikes/rest/services/world_bikeshares/FeatureServer/1", 7 | worldLayer = null, 8 | bikeshareLayer = null, 9 | lastWorldExtent = null, 10 | extentHandler = null, 11 | switchScale = 500000, 12 | defaultZoom = 3, 13 | defaultCenter = [-35, 25], 14 | worldText = "World Bikeshare View"; 15 | 16 | var mapOptions = { 17 | basemap: "gray", 18 | sliderStyle: "small", 19 | wrapAround180: true, 20 | center: defaultCenter, 21 | zoom: defaultZoom 22 | }, 23 | bikesharePopupTemplate = null; 24 | 25 | // We'll use Dojo Charting to create the symbols for the World Bikeshare Map 26 | // Thanks to Derek Swingley for the sample: 27 | // http://dl.dropboxusercontent.com/u/2654618/election-results.html 28 | 29 | // Declare our own charting theme. 30 | require(["dojox/charting/Theme"], function(Theme) { 31 | dojo.provide("geeknixta.charting.themes.WorldBikemap"); 32 | geeknixta.charting.themes.WorldBikemap = new Theme({ 33 | colors: [ 34 | "#C69F00", 35 | "#2C902C", 36 | "#7fc25d", 37 | "#60b32b", 38 | "#277085", 39 | "#333" 40 | ] 41 | }); 42 | }); 43 | 44 | // Because of the way requests are tiled, and the setTimeout workaround hack we 45 | // have to employ to read the canvas output into a PNG image, we'll use a tracking 46 | // system to know when a specific Bike Share graphic is in process or generated. 47 | var charts = {}; 48 | 49 | // This will be the renderer getSymbol() method. Given a feature (f), it will 50 | // create an off-screen chart using Dojo Charting, and then render it to a PNG. 51 | // Unfortunately, the actual canvas rendering happens after this call completes 52 | // so we actually return a null response until our "charts" symbol cache has a valid 53 | // entry, which happens on setTimeout. When we get the symbol in setTimeout we also 54 | // call setSymbol(), since the renderer's getSymbol() will already have happened. on 55 | // subsequent requests for getSymbol(), we'll have it cached so we just return it. 56 | function createPieChart(f) { 57 | if (!charts.hasOwnProperty(f.attributes.name)) { 58 | charts[f.attributes.name] = null; 59 | // quick way to find max population so pie charts 60 | // can be sized according to total state population 61 | require([ 62 | "dojox/charting/Chart", "geeknixta/charting/themes/WorldBikemap", "dojox/charting/plot2d/Pie", 63 | "dojo/dom", "dojo/dom-construct", "dojo/query", "dojo/on", "dojo/aspect", 64 | "dojo/_base/array", "dojo/_base/connect", 65 | "esri/map", "esri/tasks/query", "esri/dijit/Popup", 66 | "dojo/domReady!" 67 | ], 68 | function(Chart, theme, PiePlot, dom, domConstruct, query, on, aspect, array, connect) { 69 | 70 | // Stash a placeholder symbol while we work out what this really is. 71 | charts[f.attributes.name] = new esri.symbol.SimpleMarkerSymbol(); 72 | // modify the theme so the chart background is transparent 73 | // thanks to: http://stackoverflow.com/a/8987358/1934 74 | theme.chart.fill = "transparent"; 75 | 76 | // Some data for the chart 77 | var d = { 78 | bikes: f.attributes.bikes, 79 | docks: f.attributes.docks, 80 | network: f.attributes.name, 81 | point: f.geometry, 82 | stations: f.attributes.stations 83 | }; 84 | // Make a div to hold the chart. 85 | var size = (d.stations > 300) ? 24 : 86 | (d.stations > 150) ? 18 : 87 | 12; 88 | var chartNode = domConstruct.create("div", { 89 | id: d.network, 90 | class: "featureChart", 91 | style: { 92 | width: size * 2.1 + "px", 93 | height: size * 2.1 + "px" 94 | } 95 | }, dom.byId("charts")); 96 | // Set some attribute that will get read by Dojo Charting. 97 | chartNode.setAttribute("data-x", d.point.x); 98 | chartNode.setAttribute("data-y", d.point.y); 99 | chartNode.setAttribute("data-bikes", d.bikes); 100 | chartNode.setAttribute("data-docks", d.docks); 101 | chartNode.setAttribute("data-stations", d.stations); 102 | chartNode.setAttribute("data-size", size); 103 | 104 | // Create and render the chart. 105 | var chart = new Chart(chartNode); 106 | chart.setTheme(theme); 107 | chart.addPlot("default", { 108 | type: PiePlot, 109 | radius: size, 110 | labels: false 111 | }); 112 | chart.addSeries("default", [d.docks,d.bikes]); 113 | chart.render(); 114 | 115 | // The chart won't be ready for canvas rendering to a PNG until 116 | // the thread has dequeued some stuff. So, we'll chuck this onto the 117 | // and of that queue and get it as soon as we can. 118 | setTimeout(function() { 119 | // Render to PNG data. 120 | var imgSrc = chartNode.firstChild.toDataURL("image/png"); 121 | var imgData = imgSrc.split(",")[1]; 122 | // Create that symbol we couldn't return directly from getRenderer() 123 | var sym = new esri.symbol.PictureMarkerSymbol({ 124 | "angle": 0, 125 | "xoffset": 0, 126 | "yoffset": 0, 127 | "type": "esriPMS", 128 | "imageData": imgData, 129 | "contentType": "image/png", 130 | "width": size, 131 | "height": size, 132 | }); 133 | // Replace our cached symbol with this one. 134 | charts[f.attributes.name] = sym; 135 | // Set the symbol on the graphic we generated it for. 136 | f.setSymbol(sym); 137 | // Log that we created it. 138 | console.log("Created Symbol"); 139 | // And remove the canvas element we created above to hold the chart. 140 | domConstruct.destroy(chartNode); 141 | }, 0); 142 | }); 143 | } 144 | // We'll have something cached, whether it's the default symbol or the generated 145 | // chart symbol, so just return that. 146 | return charts[f.attributes.name]; 147 | } 148 | 149 | function getParameterByName(name) { 150 | name = name.replace(/[\[]/, "\[").replace(/[\]]/, "\]"); 151 | var regex = new RegExp("[\?&]" + name + "=([^&#]*)"), 152 | results = regex.exec(location.search); 153 | return results == null ? null : decodeURIComponent(results[1].replace(/\+/g, " ")); 154 | } 155 | 156 | function getExtent(layerEndpoint, callback) { 157 | var featureServiceDescriptionUrl = layerEndpoint + "?f=json"; 158 | var jsonfile = new XMLHttpRequest(); 159 | jsonfile.open("GET", featureServiceDescriptionUrl, true); 160 | jsonfile.onreadystatechange = function() { 161 | if (jsonfile.readyState == 4) { 162 | if (jsonfile.status == 200) { 163 | require(["esri/geometry/Extent"], function(Extent) { 164 | var extent = JSON.parse(jsonfile.responseText).extent; 165 | extent = new Extent(extent); 166 | return callback(null, extent); 167 | }); 168 | } else { 169 | return callback("Could not get extent", null); 170 | } 171 | } 172 | }; 173 | jsonfile.send(null); 174 | } 175 | 176 | function openBikeshareLayer(g) { 177 | var url = g.attributes.url; 178 | lastWorldExtent = map.extent; 179 | getExtent(url, function(err, extent) { 180 | if (!err) { 181 | if (extentHandler) { 182 | extentHandler.remove(); 183 | } 184 | require(["esri/layers/FeatureLayer", "esri/InfoTemplate"], 185 | function(FeatureLayer, InfoTemplate) { 186 | if (bikeshareLayer) { 187 | map.removeLayer(bikeshareLayer); 188 | delete bikeshareLayer; 189 | } 190 | bikeshareLayer = new FeatureLayer(url, { 191 | infoTemplate: new InfoTemplate("${bikesClass} at ${name}", 192 | "Bikes: ${bikes}
" + 193 | "Docks: ${free}") 194 | }); 195 | bikeshareLayer.world_network_details = g.attributes; 196 | bikeshareLayer.setMinScale(switchScale); 197 | bikeshareLayer.on("load", function() { 198 | require(["esri/dijit/Legend", "dojo/dom-construct"], 199 | function(Legend, domConstruct) { 200 | legend = new Legend({ 201 | map: map, 202 | layerInfos: [{ 203 | layer: bikeshareLayer 204 | }], 205 | }, domConstruct.create("div", {id: "mainLegend", class: "legend"}, map.root)); 206 | legend.startup(); 207 | }); 208 | }); 209 | map.addLayer(bikeshareLayer); 210 | map.setExtent(extent, true); 211 | }); 212 | } else { 213 | console.log("Error loading layer! " + err); 214 | } 215 | }); 216 | } 217 | 218 | function openWorldLayer() { 219 | require(["esri/layers/FeatureLayer", 220 | "esri/renderers/SimpleRenderer", "esri/symbols/SimpleMarkerSymbol"], 221 | function(FeatureLayer, SimpleRenderer, SimpleMarkerSymbol) { 222 | if (!worldLayer) { 223 | worldLayer = new FeatureLayer(worldLayerURL); 224 | document.getElementById("titleMessage").innerText = worldText; 225 | worldLayer.setMaxScale(switchScale); 226 | map.addLayer(worldLayer); 227 | worldLayer.on("click", function(e) { 228 | var g = e.graphic; 229 | if (g) { 230 | openBikeshareLayer(g); 231 | } 232 | }); 233 | worldLayer.on("scale-visibility-change", function(e) { 234 | if (worldLayer.isVisibleAtScale(map.getScale())) { 235 | document.getElementById("titleMessage").innerText = worldText; 236 | document.getElementById("userMessage").innerText = ""; 237 | map.infoWindow.hide(); 238 | if (legend) { legend.destroy() }; 239 | } else { 240 | var details = bikeshareLayer.world_network_details; 241 | document.getElementById("titleMessage").innerText = details.name; 242 | var dStr = " open dock" + (details.docks>1?"s":""); 243 | var bStr = " available bike" + (details.bikes>1?"s":""); 244 | document.getElementById("userMessage").innerHTML = details.stations + 245 | " stations
(" + details.docks + dStr + ", " + details.bikes + bStr + ")"; 246 | } 247 | }); 248 | var renderer = new SimpleRenderer(new SimpleMarkerSymbol()); 249 | renderer.getSymbol = createPieChart; 250 | worldLayer.renderer = renderer; 251 | } 252 | 253 | if (lastWorldExtent) { 254 | map.setExtent(lastWorldExtent); 255 | } else { 256 | map.centerAndZoom(defaultCenter, defaultZoom); 257 | } 258 | }); 259 | } 260 | 261 | function initApp() { 262 | require(["esri/map", "esri/dijit/InfoWindowLite", "esri/InfoTemplate", 263 | "dojo/dom-construct", "dojo/domReady!"], 264 | function(Map, InfoWindowLite, InfoTemplate, domConstruct) { 265 | map = new Map("map", mapOptions); 266 | 267 | var parameterRoot = getParameterByName("urlroot"); 268 | if (parameterRoot) { 269 | urlRoot = parameterRoot; 270 | } 271 | 272 | worldLayerURL = urlRoot + worldLayerURL; 273 | 274 | var infoWindow = new InfoWindowLite(null, domConstruct.create("div", null, null, map.root)); 275 | infoWindow.startup(); 276 | map.setInfoWindow(infoWindow); 277 | map.infoWindow.resize(350, 75); 278 | 279 | map.on("load", function() { 280 | openWorldLayer(); 281 | document.getElementById("btnBackToWorldView").onclick = openWorldLayer; 282 | }); 283 | }); 284 | } 285 | 286 | initApp(); 287 | -------------------------------------------------------------------------------- /samples/currentweather/currentwx.js: -------------------------------------------------------------------------------- 1 | var dataproviderbase = require("../../src/dataproviderbase"); 2 | var TerraformerArcGIS = require("terraformer/Parsers/ArcGIS"); 3 | var Terraformer = require("terraformer"); 4 | var childProcess = require("child_process"); 5 | var parseString = require("xml2js").parseString; 6 | var util = require("util"); 7 | var path = require("path"); 8 | var fs = require("fs"); 9 | 10 | CurrentWx = function(){ 11 | CurrentWx.super_.call(this); 12 | 13 | this._fieldCacheFilename = path.join(path.dirname(module.filename),"data","fields.json"); 14 | this._wxProviderFilename = path.join(path.dirname(module.filename),"data","allstations.xml"); 15 | this._fieldInfos = null; 16 | this._wxProvidersInfos = null; 17 | this._wxStations = []; 18 | this._wxStationsArr = []; 19 | this._rawStationsXML = null; 20 | this._wxDataRaw = null; 21 | this._child = null; //fork of childProcess 22 | this._retrieveChild = null; 23 | 24 | /** 25 | * Local ENUMs (Constants) 26 | * @type {Object} 27 | * @returns {*} 28 | */ 29 | this._localEnum = function(){ 30 | var values = { 31 | BACKGROUND_TIME_TICK: 30 * 60 * 1000 /* Use shorter interval for testing. E.g. 5 * 1000 */ 32 | } 33 | 34 | return values; 35 | }; 36 | 37 | this._parseStationsXML = function(data,type,callback){ 38 | parseString(data,function(err, result){ 39 | if(err)console.log("_parseStationsXML error: " + err); 40 | var json = JSON.stringify(result); 41 | 42 | if(type == 'string'){ 43 | callback(json,err); 44 | } 45 | else{ 46 | callback(result,err); 47 | } 48 | }) 49 | } 50 | 51 | /** 52 | * Executed upon a Query request from a client application 53 | * @param query 54 | * @param data 55 | * @param callback 56 | * @returns {Array} 57 | * @private 58 | */ 59 | this._loopWxStations = function(/* Query */query, data, callback){ 60 | if(query.geometryType === "esriGeometryEnvelope"){ 61 | var queryGeom= query.geometry; 62 | 63 | var primitive = new Terraformer.Polygon( { 64 | "type":"Polygon", 65 | "coordinates": [ 66 | [ 67 | [ queryGeom.xmin, queryGeom.ymax ], 68 | [ queryGeom.xmax, queryGeom.ymax ], 69 | [ queryGeom.xmax, queryGeom.ymin ], 70 | [ queryGeom.xmin, queryGeom.ymin ], 71 | [ queryGeom.xmin, queryGeom.ymax ] 72 | ] 73 | ] 74 | }) 75 | 76 | Terraformer.toGeographic(primitive); 77 | console.log("PRIMITIVE: " + primitive.coordinates); 78 | 79 | var arrayTemp = []; 80 | 81 | for(var t in data){ 82 | 83 | if(data[t] === "undefined" || data[t] === null || typeof data[t] === "undefined"){ 84 | console.log("_loopWxStations value " + t + " = undefined"); 85 | } 86 | else{ 87 | 88 | var tempObject = data[t].current_observation; 89 | var point = new Terraformer.Point([tempObject.longitude, tempObject.latitude]); 90 | var test = point.within(primitive); 91 | 92 | var test = true; 93 | if(test){ 94 | var feature = this._createWxStationFeature(tempObject); 95 | 96 | arrayTemp.push(feature); 97 | 98 | } 99 | } 100 | } 101 | console.log("Number of stations returned within extent: " + arrayTemp.length); 102 | return arrayTemp; 103 | } 104 | 105 | if(typeof query.geometryType === "undefined"){ 106 | var arrayTemp = []; 107 | 108 | for(var t in data){ 109 | 110 | if(data[t] === "undefined" || data[t] === null || typeof data[t] === "undefined"){ 111 | console.log("_loopWxStations value " + t + " = undefined"); 112 | } 113 | else{ 114 | 115 | var tempObject = data[t].current_observation; 116 | 117 | var feature = this._createWxStationFeature(tempObject); 118 | 119 | arrayTemp.push(feature); 120 | 121 | } 122 | } 123 | console.log("Number of stations returned within extent: " + arrayTemp.length); 124 | return arrayTemp; 125 | } 126 | else{ 127 | console.log("_loopWxStations: query does not contain an acceptable geometry type."); 128 | } 129 | } 130 | 131 | /** 132 | * Creates a feature object for each station. This uses a bit of brute force to ensure 133 | * attribute names are user friendly. Easier way is to simply pass-thru the attributes all at once. 134 | * @param data 135 | * @returns {} 136 | * @private 137 | */ 138 | this._createWxStationFeature = function(/* Object */ data){ 139 | 140 | return feature = { 141 | geometry: { 142 | x: typeof data.longitude === "undefined" ? data.longitude : data.longitude[0], 143 | y: typeof data.latitude === "undefined" ? data.latitude : data.latitude[0], 144 | spatialReference:{ 145 | wkid:4326 146 | } 147 | }, 148 | attributes:{ 149 | id: typeof data.station_id === "undefined" ? data.station_id : data.station_id[0], 150 | locationText: typeof data.location === "undefined" ? data.location : data.location[0], 151 | windDirection: typeof data.wind_dir === "undefined" ? data.wind_dir : data.wind_dir[0], 152 | latitude: typeof data.latitude === "undefined" ? data.latitude : data.latitude[0], 153 | longitude: typeof data.longitude === "undefined" ? data.longitude : data.longitude[0], 154 | pickupTimeMinutes: typeof data.suggested_pickup_period === "undefined" ? data.suggested_pickup_period : data.suggested_pickup_period[0], 155 | time: typeof data.observation_time_rfc822 === "undefined" ? data.observation_time_rfc822 : data.observation_time_rfc822[0], 156 | weather: typeof data.weather === "undefined" ? data.weather : data.weather[0], 157 | tempF: typeof data.temp_f === "undefined" ? data.temp_f : data.temp_f[0], 158 | tempC: typeof data.temp_c === "undefined" ? data.temp_c : data.temp_c[0], 159 | relativeHumidity: typeof data.relative_humidity === "undefined" ? data.relative_humidity : data.relative_humidity[0], 160 | windDirection: typeof data.wind_dir === "undefined" ? data.wind_dir : data.wind_dir[0], 161 | windDegrees: typeof data.wind_degrees === "undefined" ? data.wind_degrees : data.wind_degrees[0], 162 | windMPH: typeof data.wind_mph === "undefined" ? data.wind_mph : data.wind_mph[0], 163 | windKnots: typeof data.wind_kt === "undefined" ? data.wind_kt : data.wind_kt[0], 164 | pressureMb: typeof data.pressure_mb === "undefined" ? data.pressure_mb : data.pressure_mb[0], 165 | pressureIn: typeof data.pressure_in === "undefined" ? data.pressure_in : data.pressure_in[0], 166 | dewPointF: typeof data.dewpoint_f === "undefined" ? data.dewpoint_f : data.dewpoint_f[0], 167 | dewPointC: typeof data.dewpoint_c === "undefined" ? data.dewpoint_c : data.dewpoint_c[0], 168 | heatIndexF: typeof data.heat_index_f === "undefined" ? data.heat_index_f : data.heat_index_f[0], 169 | heatIndexC: typeof data.heat_index_c === "undefined" ? data.heat_index_c : data.heat_index_c[0], 170 | visibilityMi: typeof data.visibility_mi === "undefined" ? data.visibility_mi : data.visibility_mi[0], 171 | twoDayHistoryURL: typeof data.two_day_history_url === "undefined" ? data.two_day_history_url : data.two_day_history_url[0] 172 | } 173 | }; 174 | } 175 | 176 | this._setRetrieveChildMsgHandler = function(){ 177 | 178 | this._retrieveChild.on('message', function(msg){ 179 | console.log("Station data received by parent process."); 180 | //console.log(msg[0]["current_observation"]['latitude']); 181 | this._wxDataRaw = msg; 182 | }.bind(this)) 183 | 184 | } 185 | 186 | this._startTimer = function(){ 187 | 188 | this._child = childProcess.fork("./samples/currentweather/utils/timer"); 189 | 190 | this._child.on('message', function(msg){ 191 | if(msg == "tick"){ 192 | console.log("timer tick event recv'd"); 193 | try{ 194 | var data = { 195 | "content":this._rawStationsXML 196 | } 197 | 198 | this._retrieveChild.send(data); 199 | } 200 | catch(err){ 201 | console.log("_startTimer Error: " + err.message + "\n " + err.stack); 202 | } 203 | 204 | } 205 | }.bind(this)) 206 | 207 | var interval = this._localEnum().BACKGROUND_TIME_TICK; 208 | 209 | var startParams = { 210 | "start":true, 211 | "interval":interval 212 | } 213 | 214 | this._child.send(startParams); 215 | } 216 | 217 | /** 218 | * Initializes the library. 219 | * IMPORTANT: Make sure this is at the bottom of the CurrentWx so that all functions and variables are inherited 220 | * @type {} 221 | */ 222 | this.init = function(){ 223 | 224 | this._retrieveChild = childProcess.fork("./samples/currentweather/utils/retriever"); 225 | 226 | if (fs.existsSync(this._fieldCacheFilename)) 227 | { 228 | this._fieldInfos = JSON.parse(fs.readFileSync(this._fieldCacheFilename, 'utf8')); 229 | console.log("Loaded field info from " + this._fieldCacheFilename); 230 | } 231 | else{ 232 | console.log("WARNING: Unable to load field info from fields.json, " + this._fieldCacheFilename); 233 | } 234 | 235 | if(fs.existsSync(this._wxProviderFilename)){ 236 | 237 | var temp = fs.readFileSync(this._wxProviderFilename); 238 | 239 | this._parseStationsXML(temp,null,function(data,err){ 240 | 241 | if(err){ 242 | console.log("Init() Error parsing xml: " + err); 243 | } 244 | else{ 245 | //this._createWxStationsArr(data); 246 | this._rawStationsXML = data; 247 | this._setRetrieveChildMsgHandler(); 248 | 249 | var data = { 250 | "content":this._rawStationsXML 251 | } 252 | 253 | this._retrieveChild.send(data); 254 | } 255 | }.bind(this)) 256 | 257 | console.log("Loaded weather provider info from " + this._wxProviderFilename); 258 | } 259 | else{ 260 | console.log("WARNING: Unable to load weather provider info from allstations.xml, " + this._wxProviderFilename); 261 | } 262 | 263 | console.log("Initialized Current Weather provider"); 264 | 265 | this._startTimer(); 266 | 267 | }.bind(this)(); 268 | 269 | } 270 | 271 | // This node.js helper function allows us to inherit from dataproviderbase. 272 | util.inherits(CurrentWx, dataproviderbase.DataProviderBase); 273 | 274 | // And now we'll override only what we need to (see also /src/dataproviderbase.js). 275 | Object.defineProperties(CurrentWx.prototype, { 276 | name: { 277 | get: function() { 278 | // Override the service name - every data provider should override this. 279 | return "currentwx"; 280 | } 281 | }, 282 | getServiceIds: { 283 | value: function(callback) { 284 | // Override the service name - every data provider should override this. 285 | callback([this.name]); 286 | } 287 | }, 288 | 289 | fields: { 290 | value: function(serviceId, layerId) { 291 | // These are the fields that the single layer of each FeatureService will return. 292 | // this could be different for each feature service and layer. 293 | return this._fieldInfos; 294 | } 295 | }, 296 | idField: { 297 | value: function(serviceId, layerId) { 298 | return "id"; 299 | } 300 | }, 301 | nameField: { 302 | value: function(serviceId, layerId) { 303 | return "locationText"; 304 | } 305 | }, 306 | fields: { 307 | value: function(serviceId, layerId) { 308 | return this._fieldInfos; 309 | } 310 | }, 311 | getLayerIds: { 312 | value: function(serviceId, callback) { 313 | callback([0], null); 314 | } 315 | }, 316 | isReady: { 317 | get: function() { 318 | // Since we depend on some async stuff, we might not be ready immediately. 319 | // We'll track our readiness in the constructor and return whatever that says 320 | // is the case. 321 | return this._isReady; 322 | } 323 | }, 324 | // getLayerName: { 325 | // value: function(serviceId, layerId) { 326 | // var c = parseServiceId(serviceId); 327 | // serviceId = c.serviceId; 328 | // switch (serviceId) { 329 | // case "repo": 330 | // return "Repo Layer " + layerId; 331 | // case "gist": 332 | // return "Gist " + c.gistId + " File " + layerId; 333 | // } 334 | // } 335 | // }, 336 | getFeatureServiceDetails: { 337 | value: function(detailsTemplate, serviceId, callback) { 338 | 339 | var provider = this; 340 | this.getLayerIds(serviceId, function(layerIds, err) { 341 | callback(layerIds, provider.getLayerNamesForIds(serviceId, layerIds), err); 342 | }); 343 | } 344 | }, 345 | 346 | getFeatureServiceLayerDetails: { 347 | value: function(detailsTemplate, serviceId, layerId, callback) { 348 | // We'll take the default JSON that the engine has calculated for us, but we'll 349 | // inject an extent if we have one stored so that clients can connect to us 350 | // more easily. 351 | callback({ 352 | layerName: this.getLayerName(serviceId, layerId), 353 | idField: this.idField(serviceId, layerId), 354 | nameField: this.nameField(serviceId, layerId), 355 | fields: this.fields(serviceId, layerId) 356 | }, null); 357 | } 358 | }, 359 | 360 | featuresForQuery: { 361 | value: function(serviceId, layerId, query, callback) { 362 | console.log("Query timestamp via currentwx.js: " + new Date().toUTCString()) 363 | var wxResult = this._loopWxStations(query,this._wxDataRaw); 364 | var err = ""; 365 | var provider = this; 366 | var idField = provider.idField(serviceId, layerId); 367 | var fields = provider.fields(serviceId, layerId); 368 | callback(wxResult, idField, fields, err); 369 | 370 | } 371 | } 372 | }); 373 | 374 | 375 | exports.CurrentWx = CurrentWx; 376 | -------------------------------------------------------------------------------- /src/output.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var util = require('util'); 3 | 4 | var dataproviderbase = require("./dataproviderbase"); 5 | 6 | var terraformer = require("terraformer"), 7 | terraformerArcGIS = require("terraformer/Parsers/ArcGIS"); 8 | 9 | var _infoJSON = JSON.parse(fs.readFileSync('resources/templates/info.json', 'utf8')); 10 | var _servicesJSON = JSON.parse(fs.readFileSync('resources/templates/services.json', 'utf8')); 11 | var _featureServiceJSON = JSON.parse(fs.readFileSync('resources/templates/featureService.json', 'utf8')); 12 | var _featureService_LayerItemJSON = JSON.parse(fs.readFileSync('resources/templates/featureService_layerItem.json', 'utf8')); 13 | var _featureServiceLayerJSON = JSON.parse(fs.readFileSync('resources/templates/featureServiceLayer.json', 'utf8')); 14 | var _featureServiceLayersJSON = JSON.parse(fs.readFileSync('resources/templates/featureServiceLayers.json', 'utf8')); 15 | 16 | var _featureSetJSON = JSON.parse(fs.readFileSync('resources/templates/featureSet.json', 'utf8')); 17 | var _queryCountJSON = JSON.parse(fs.readFileSync('resources/templates/queryCount.json', 'utf8')); 18 | var _queryIdsJSON = JSON.parse(fs.readFileSync('resources/templates/queryIds.json', 'utf8')); 19 | 20 | var _dataProvidersHTML = fs.readFileSync('resources/templates/dataProviders.html', 'utf8'); 21 | 22 | var _infoHTML = fs.readFileSync('resources/templates/info.html', 'utf8'); 23 | var _servicesHTML = fs.readFileSync('resources/templates/services.html', 'utf8'); 24 | var _featureServiceHTML = fs.readFileSync('resources/templates/featureService.html', 'utf8'); 25 | var _featureServiceLayerHTML = fs.readFileSync('resources/templates/featureServiceLayer.html', 'utf8'); 26 | var _featureServiceLayersHTML = fs.readFileSync('resources/templates/featureServiceLayers.html', 'utf8'); 27 | var _featureServiceLayer_LayerItemHTML = fs.readFileSync('resources/templates/featureServiceLayer_layerItem.html', 'utf8'); 28 | 29 | var _serviceDetailsJSON = { 30 | "name": "dummyService", 31 | "type": "FeatureServer", 32 | "url": "http://www.arcgis.com" 33 | }; 34 | 35 | var drawingInfo_Point = JSON.parse(fs.readFileSync("resources/templates/drawingInfo/point.json", 'utf8')); 36 | var drawingInfo_Line = JSON.parse(fs.readFileSync("resources/templates/drawingInfo/line.json", 'utf8')); 37 | var drawingInfo_Polygon = JSON.parse(fs.readFileSync("resources/templates/drawingInfo/polygon.json", 'utf8')); 38 | 39 | function _clone(object) { 40 | if (object) { 41 | return JSON.parse(JSON.stringify(object)); 42 | } 43 | return null; 44 | } 45 | 46 | var envelopeHTMLTemplate = '
    XMin: %d
    YMin: %d
    XMax: %d
    YMax: %d
    ' + 47 | 'Spatial Reference: %d
'; 48 | function htmlStringForEnvelope(env) { 49 | return util.format(envelopeHTMLTemplate, 50 | env.xmin, env.ymin, 51 | env.xmax, env.ymax, 52 | env.spatialReference.wkid); 53 | } 54 | 55 | var fieldHTMLTemplate = '
  • %s (type: %s, alias: %s, nullable: %s, editable: %s)
  • \n'; 56 | 57 | function getHtmlForFields(fields) { 58 | var outStr = ""; 59 | for (var i=0; i < fields.length; i++) 60 | { 61 | var field = fields[i]; 62 | outStr = outStr + util.format(fieldHTMLTemplate, 63 | field.name, 64 | field.type, 65 | field.alias, 66 | field.nullable, 67 | false); 68 | } 69 | return outStr; 70 | }; 71 | 72 | 73 | 74 | // JSON 75 | function infoJSON(dataProvider, callback) { 76 | var t = _clone(_infoJSON); 77 | t["currentVersion"] = dataProvider.serverVersion; 78 | t["fullVersion"] = dataProvider.serverVersion.toString(); 79 | callback(t, null); 80 | } 81 | 82 | function servicesJSON(dataProvider, callback) { 83 | var t = _clone(_servicesJSON); 84 | t["currentVersion"] = dataProvider.serverVersion; 85 | var serviceIds = dataProvider.getServiceIds(function(serviceIds, err) { 86 | for (var i=0; i -1) { 385 | return true; 386 | } 387 | return false; 388 | } else { 389 | return true; 390 | } 391 | }, 392 | 393 | // Return an array of IDs matching the features that would be returned by the Query. 394 | // This is called when returnIdsOnly=true is passed as a parameter. It is ignored if 395 | // returnCountOnly is also true. 396 | // 397 | // This default implementation merely calls _featuresForQuery() and returns an array 398 | // of IDs instead of an array of features. 399 | // 400 | // Override with a more efficient method if your dataprovider allows for it. 401 | idsForQuery: function(serviceId, layerId, query, callback) { 402 | if (!this._devMode) console.log("Implement iDsForQuery to return an error of Object IDs"); 403 | var thisDataProvider = this; 404 | this._featuresForQuery(serviceId, layerId, query, function (results, idField, fields, err) { 405 | var r = []; 406 | if (!err) { 407 | for (var i=0; i .radio:first-child, 593 | .controls > .checkbox:first-child { 594 | padding-top: 5px; 595 | } 596 | 597 | .radio.inline, 598 | .checkbox.inline { 599 | display: inline-block; 600 | padding-top: 5px; 601 | margin-bottom: 0; 602 | vertical-align: middle; 603 | } 604 | 605 | .radio.inline + .radio.inline, 606 | .checkbox.inline + .checkbox.inline { 607 | margin-left: 10px; 608 | } 609 | 610 | .input-mini { 611 | width: 60px; 612 | } 613 | 614 | .input-small { 615 | width: 90px; 616 | } 617 | 618 | .input-medium { 619 | width: 130px; 620 | } 621 | 622 | .input-large { 623 | width: 210px; 624 | } 625 | 626 | .input-xlarge { 627 | width: 270px; 628 | } 629 | 630 | .input-xxlarge { 631 | width: 530px; 632 | } 633 | 634 | input[class*="span"], 635 | select[class*="span"], 636 | textarea[class*="span"], 637 | .uneditable-input[class*="span"], 638 | .row-fluid input[class*="span"], 639 | .row-fluid select[class*="span"], 640 | .row-fluid textarea[class*="span"], 641 | .row-fluid .uneditable-input[class*="span"] { 642 | float: none; 643 | margin-left: 0; 644 | } 645 | 646 | .input-append input[class*="span"], 647 | .input-append .uneditable-input[class*="span"], 648 | .input-prepend input[class*="span"], 649 | .input-prepend .uneditable-input[class*="span"], 650 | .row-fluid input[class*="span"], 651 | .row-fluid select[class*="span"], 652 | .row-fluid textarea[class*="span"], 653 | .row-fluid .uneditable-input[class*="span"], 654 | .row-fluid .input-prepend [class*="span"], 655 | .row-fluid .input-append [class*="span"] { 656 | display: inline-block; 657 | } 658 | 659 | input, 660 | textarea, 661 | .uneditable-input { 662 | margin-left: 0; 663 | margin-bottom: 4px; 664 | } 665 | 666 | input[disabled], 667 | select[disabled], 668 | textarea[disabled], 669 | input[readonly], 670 | select[readonly], 671 | textarea[readonly] { 672 | cursor: not-allowed; 673 | background-color: #eeeeee; 674 | } 675 | 676 | input[type="radio"][disabled], 677 | input[type="checkbox"][disabled], 678 | input[type="radio"][readonly], 679 | input[type="checkbox"][readonly] { 680 | background-color: transparent; 681 | } 682 | 683 | /********************************************************************/ 684 | /*** Buttons ***/ 685 | 686 | .fade { 687 | opacity: 0; 688 | -webkit-transition: opacity 0.15s linear; 689 | -moz-transition: opacity 0.15s linear; 690 | -o-transition: opacity 0.15s linear; 691 | transition: opacity 0.15s linear; 692 | } 693 | 694 | .fade.in { 695 | opacity: 1; 696 | } 697 | 698 | .collapse { 699 | position: relative; 700 | height: 0; 701 | overflow: hidden; 702 | -webkit-transition: height 0.35s ease; 703 | -moz-transition: height 0.35s ease; 704 | -o-transition: height 0.35s ease; 705 | transition: height 0.35s ease; 706 | } 707 | 708 | .collapse.in { 709 | height: auto; 710 | } 711 | 712 | .close { 713 | float: right; 714 | font-size: 20px; 715 | font-weight: bold; 716 | line-height: 20px; 717 | color: #000000; 718 | text-shadow: 0 1px 0 #ffffff; 719 | opacity: 0.2; 720 | filter: alpha(opacity=20); 721 | } 722 | 723 | .close:hover { 724 | color: #000000; 725 | text-decoration: none; 726 | cursor: pointer; 727 | opacity: 0.4; 728 | filter: alpha(opacity=40); 729 | } 730 | 731 | button.close { 732 | padding: 0; 733 | cursor: pointer; 734 | background: transparent; 735 | border: 0; 736 | -webkit-appearance: none; 737 | } 738 | 739 | 740 | .btn { 741 | display: inline-block; 742 | *display: inline; 743 | /*padding: 4px 14px;*/ 744 | padding: 2px 12px; /* small */ 745 | *margin-left: .3em;*/ 746 | margin: 5px 5px 5px 5px; /* small */ 747 | font-size: 12px; /* small */ 748 | /*line-height: 20px;*/ 749 | line-height: 18px; /* small */ 750 | /* *line-height: 20px; */ 751 | *line-height: 18px; /* small */ 752 | color: #333333; 753 | text-align: center; 754 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); 755 | vertical-align: middle; 756 | cursor: pointer; 757 | background-color: #f5f5f5; 758 | *background-color: #e6e6e6; 759 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); 760 | background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); 761 | background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); 762 | background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); 763 | background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); 764 | background-repeat: repeat-x; 765 | border: 1px solid #bbbbbb; 766 | *border: 0; 767 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 768 | border-color: #e6e6e6 #e6e6e6 #bfbfbf; 769 | border-bottom-color: #a2a2a2; 770 | -webkit-border-radius: 4px; 771 | -moz-border-radius: 4px; 772 | border-radius: 4px; 773 | filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); 774 | filter: progid:dximagetransform.microsoft.gradient(enabled=false); 775 | *zoom: 1; 776 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 777 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 778 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); 779 | } 780 | 781 | .btn:hover, 782 | .btn:active, 783 | .btn.active, 784 | .btn.disabled, 785 | .btn[disabled] { 786 | color: #333333; 787 | background-color: #e6e6e6; 788 | *background-color: #d9d9d9; 789 | } 790 | 791 | .btn:active, 792 | .btn.active { 793 | background-color: #cccccc \9; 794 | } 795 | 796 | .btn:first-child { 797 | *margin-left: 0; 798 | } 799 | 800 | .btn:hover { 801 | color: #333333; 802 | text-decoration: none; 803 | background-color: #e6e6e6; 804 | *background-color: #d9d9d9; 805 | /* Buttons in IE7 don't get borders, so darken on hover */ 806 | 807 | background-position: 0 -15px; 808 | -webkit-transition: background-position 0.1s linear; 809 | -moz-transition: background-position 0.1s linear; 810 | -o-transition: background-position 0.1s linear; 811 | transition: background-position 0.1s linear; 812 | } 813 | 814 | .btn:focus { 815 | outline: thin dotted #333; 816 | outline: 5px auto -webkit-focus-ring-color; 817 | outline-offset: -2px; 818 | } 819 | 820 | .btn.active, 821 | .btn:active { 822 | background-color: #e6e6e6; 823 | background-color: #d9d9d9 \9; 824 | background-image: none; 825 | outline: 0; 826 | -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); 827 | -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); 828 | box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); 829 | } 830 | 831 | .btn.disabled, 832 | .btn[disabled] { 833 | cursor: default; 834 | background-color: #e6e6e6; 835 | background-image: none; 836 | opacity: 0.65; 837 | filter: alpha(opacity=65); 838 | -webkit-box-shadow: none; 839 | -moz-box-shadow: none; 840 | box-shadow: none; 841 | } 842 | 843 | .btn-large { 844 | padding: 9px 14px; 845 | font-size: 16px; 846 | line-height: normal; 847 | -webkit-border-radius: 5px; 848 | -moz-border-radius: 5px; 849 | border-radius: 5px; 850 | } 851 | 852 | .btn-small { 853 | padding: 3px 9px; 854 | font-size: 12px; 855 | line-height: 18px; 856 | } 857 | 858 | .btn-primary.active, 859 | .btn-warning.active, 860 | .btn-danger.active, 861 | .btn-success.active, 862 | .btn-info.active, 863 | .btn-inverse.active { 864 | color: rgba(255, 255, 255, 0.75); 865 | } 866 | 867 | .btn { 868 | border-color: #c5c5c5; 869 | border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25); 870 | } 871 | 872 | .btn-primary { 873 | /*min-width: 80px;*/ 874 | color: #ffffff; 875 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 876 | background-color: #006dcc; 877 | *background-color: #0044cc; 878 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 879 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 880 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 881 | background-image: linear-gradient(to bottom, #0088cc, #0044cc); 882 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 883 | background-repeat: repeat-x; 884 | border-color: #0044cc #0044cc #002a80; 885 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 886 | filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); 887 | filter: progid:dximagetransform.microsoft.gradient(enabled=false); 888 | } 889 | 890 | .btn-primary:hover, 891 | .btn-primary:active, 892 | .btn-primary.active, 893 | .btn-primary.disabled, 894 | .btn-primary[disabled] { 895 | color: #ffffff; 896 | background-color: #0044cc; 897 | *background-color: #003bb3; 898 | } 899 | 900 | 901 | .btn-inverse { 902 | color: #ffffff; 903 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 904 | background-color: #363636; 905 | *background-color: #222222; 906 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); 907 | background-image: -webkit-linear-gradient(top, #444444, #222222); 908 | background-image: -o-linear-gradient(top, #444444, #222222); 909 | background-image: linear-gradient(to bottom, #444444, #222222); 910 | background-image: -moz-linear-gradient(top, #444444, #222222); 911 | background-repeat: repeat-x; 912 | border-color: #222222 #222222 #000000; 913 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 914 | filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); 915 | filter: progid:dximagetransform.microsoft.gradient(enabled=false); 916 | } 917 | 918 | .btn-inverse:hover, 919 | .btn-inverse:active, 920 | .btn-inverse.active, 921 | .btn-inverse.disabled, 922 | .btn-inverse[disabled] { 923 | color: #ffffff; 924 | background-color: #222222; 925 | *background-color: #151515; 926 | } 927 | 928 | .btn-inverse:active, 929 | .btn-inverse.active { 930 | background-color: #080808 \9; 931 | } 932 | 933 | .btn-primary:active, 934 | .btn-primary.active { 935 | background-color: #003399 \9; 936 | } 937 | 938 | 939 | button.btn, 940 | input[type="submit"].btn { 941 | *padding-top: 3px; 942 | *padding-bottom: 3px; 943 | } 944 | 945 | button.btn::-moz-focus-inner, 946 | input[type="submit"].btn::-moz-focus-inner { 947 | padding: 0; 948 | border: 0; 949 | } 950 | 951 | button.btn.btn-large, 952 | input[type="submit"].btn.btn-large { 953 | *padding-top: 7px; 954 | *padding-bottom: 7px; 955 | } 956 | 957 | button.btn.btn-small, 958 | input[type="submit"].btn.btn-small { 959 | *padding-top: 3px; 960 | *padding-bottom: 3px; 961 | } 962 | 963 | button.btn.btn-mini, 964 | input[type="submit"].btn.btn-mini { 965 | *padding-top: 1px; 966 | *padding-bottom: 1px; 967 | } 968 | 969 | .btn-wide { 970 | min-width: 150px; 971 | } 972 | 973 | .btn-extra-wide { 974 | min-width: 200px; 975 | } 976 | 977 | .btn-square { 978 | margin-left: 0px; 979 | margin-right: 0px; 980 | -webkit-border-radius: 3px; 981 | -moz-border-radius: 3px; 982 | border-radius: 3px; 983 | } 984 | 985 | /*** Scrollbar ***/ 986 | ::-webkit-scrollbar{ width: 10px; height: 10px;} 987 | ::-webkit-scrollbar-track-piece{ background-color: #DBDBDB; -webkit-border-radius: 0; } 988 | ::-webkit-scrollbar-thumb:vertical{ height: 5px; background-color: #666; -webkit-border-radius: 3px; } 989 | ::-webkit-scrollbar-thumb:vertical:hover{ background-color: #999; } 990 | ::-webkit-scrollbar-thumb:horizontal{ width: 5px; background-color: #666; -webkit-border-radius: 3px; } 991 | ::-webkit-scrollbar-thumb:horizontal:hover{ background-color: #999; } -------------------------------------------------------------------------------- /samples/geohub/geohubprovider.js: -------------------------------------------------------------------------------- 1 | var util = require("util"), 2 | dataproviderbase = require("../../src/dataproviderbase"), 3 | Geohub = require("geohub"), 4 | TerraformerArcGIS = require("terraformer/Parsers/ArcGIS"), 5 | Terraformer = require("terraformer"); 6 | 7 | var geohubRepoDescription = "Use the following query parameters to output GeoServices info from a GeoJSON Source:" + "
      " + "
    • githubUser: The repo owner's username
    • " + "
    • repoName: The name of the repo containing the GeoJSON file
    • " + "
    • filePath: The path to the GeoJSON file within the repo
    • " + "
    • geoJSONType (optional): The geoJSON Geometry Type to extract (since a FeatureLayer may emit a featureset with only a single geometry type). " + "If this is omitted, the first geoJSON Geometry will define the type used to filter on. Note, this is ignored if f=geojson. It should be a valid geoJSON type as defined in the geoJSON specification (including case-sensitivity). GeometryCollection is not supported.
    • " + "
    " + "You can also specify the URL as .../geohub/rest/services/repo+githubUsername+repoName+filePath+geoJSONType/FeatureService/0
    " + "If you use the URL approach, encode path separators in the filePath portion of the URL"; 8 | 9 | var geohubGistDescription = "Use the following query parameters to output GeoServices info from a GeoJSON Source:" + "
      " + "
    • gistId: The unique ID of the Gist
    • " + "
    • geoJSONType (optional): The geoJSON Geometry Type to extract (since a FeatureLayer may emit a featureset with only a single geometry type). " + "If this is omitted, the first geoJSON Geometry will define the type used to filter on. Note, this is ignored if f=geojson. It should be a valid geoJSON type as defined in the geoJSON specification (including case-sensitivity). GeometryCollection is not supported.
    • " + "
    " + "A Gist may include many geoJSON files. The Layer Index is used to return the correct file starting with index 0. " + "If the requested format is geojson (f=geojson) then you may use * for the layerId to return all encountered geoJSON in the gist." + "
    You can also specify the URL as .../geohub/rest/services/gist+gistId+geoJSONType/FeatureService/gistFileIndex" + "
    For example, to find the 2nd file in gist 6178185, use this URL:
    • .../geohub/rest/services/gist+6178185/FeatureService/1
    "; 10 | 11 | var globalEnvelope = { 12 | "type": "Polygon", 13 | "coordinates": [ 14 | [ 15 | [-180, 90.0], [180.0, 90.0], 16 | [180.0, -90.0], [-180, -90.0], 17 | [-180.0, 90.0] 18 | ] 19 | ] 20 | }; 21 | 22 | var defaultIdFields = ["objectid", "fid", "id"]; 23 | var validIdFieldTypes = ["esriFieldTypeDouble","esriFieldTypeInteger"]; 24 | 25 | var fieldTypes = { 26 | 'string': 'esriFieldTypeString', 27 | 'integer': 'esriFieldTypeInteger', 28 | 'date': 'esriFieldTypeDate', 29 | 'datetime': 'esriFieldTypeDate', 30 | 'float': 'esriFieldTypeDouble' 31 | }; 32 | 33 | var fieldTypePriorities = { 34 | "esriFieldTypeString": [ 35 | "esriFieldTypeDouble", 36 | "esriFieldTypeInteger", 37 | "esriFieldTypeDate" 38 | ], 39 | "esriFieldTypeOID": ["esriFieldTypeInteger","esriFieldTypeDouble"], 40 | "esriFieldTypeDouble": ["esriFieldTypeInteger","esriFieldTypeDate"], 41 | "esriFieldTypeInteger": ["esriFieldTypeDate"] 42 | }; 43 | 44 | function parseServiceId(serviceId, layerId) { 45 | var r = { 46 | fullServiceId: serviceId, 47 | typelessFullServiceId: serviceId, 48 | cacheId: serviceId 49 | }; 50 | var parts = serviceId.split("+"); 51 | r.serviceId = parts[0]; 52 | switch (r.serviceId) { 53 | case "repo": 54 | if (parts.length >= 4) { 55 | r.githubUser = parts[1]; 56 | r.repoName = parts[2]; 57 | r.filePath = parts[3]; 58 | } 59 | r.geoJSONType = (parts.length > 4) ? parts[4] : null; 60 | r.name = util.format("%s::%s::%s", r.githubUser, r.repoName, r.filePath); 61 | break; 62 | case "gist": 63 | if (typeof layerId !== "undefined" && layerId !== null) { 64 | r.cacheId += "_" + layerId; 65 | } 66 | r.gistId = parts[1]; 67 | r.geoJSONType = (parts.length > 2) ? parts[2] : null; 68 | r.name = util.format("%s", r.gistId); 69 | break; 70 | }; 71 | 72 | if (r.geoJSONType) { 73 | r.name = util.format("%s::%s", r.name, r.geoJSONType); 74 | r.typelessFullServiceId = r.typelessFullServiceId.slice(0, r.typelessFullServiceId.lastIndexOf("+")); 75 | } 76 | // console.log(r); 77 | return r; 78 | }; 79 | 80 | GeoHubProvider = function(app) { 81 | GeoHubProvider.super_.call(this); 82 | // We want routes like this: 83 | // http://localhost:1337/geohub/rest/services/repo+chelm+grunt-geo+forks/FeatureServer/0 84 | // and 85 | // http://localhost:1337/geohub/rest/services/gist+6178185/FeatureServer/0 86 | this._services = { 87 | "repo": { 88 | 0: "Not Used" 89 | }, 90 | "gist": { 91 | 0: "gistFileIndex" 92 | } 93 | }; 94 | 95 | this._gistCaches = []; 96 | 97 | this.getGistCache = function(serviceId, layerId) { 98 | } 99 | 100 | console.log("Initialized new GeoHub Data Provider"); 101 | }; 102 | 103 | 104 | function addId(geoJSON, seedId) { 105 | if (arguments.length < 2) { 106 | seedId = 0; 107 | } 108 | if (!geoJSON.hasOwnProperty("id")) { 109 | geoJSON["id"] = seedId; 110 | } else { 111 | console.log("Existing ID " + seedId); 112 | } 113 | if (geoJSON.type === "FeatureCollection") { 114 | for (var i=0; i -1; 154 | }); 155 | return geoJSONItem; 156 | } 157 | else if (geoJSONItem.type === geometryType) { 158 | return geoJSONItem; 159 | } 160 | return null; 161 | }; 162 | 163 | function getEsriGeometryType(geoJSONType) { 164 | var r = null; 165 | switch (geoJSONType) { 166 | case "Point": 167 | case "MultiPoint": 168 | r = "esriGeometryPoint"; 169 | break; 170 | case "LineString": 171 | case "MultiLineString": 172 | r = "esriGeometryPolyline"; 173 | break; 174 | case "Polygon": 175 | case "MultiPolygon": 176 | r = "esriGeometryPolygon"; 177 | break; 178 | } 179 | return r; 180 | } 181 | 182 | function calculateTypeInfo(geoJSONOutput, query) { 183 | // GeoJSON FeatureCollections can contain anything. We'll 184 | // limit the output to the type of just the first item. 185 | var types = GeoJSONGeometryTypes(geoJSONOutput); 186 | var type = types[0]; 187 | if (arguments.length > 1 && query && 188 | query.hasOwnProperty("geohubParams") && 189 | query.geohubParams.geoJSONType) { 190 | type = query.geohubParams.geoJSONType; 191 | } 192 | 193 | return { 194 | type: type, 195 | types: types 196 | }; 197 | } 198 | 199 | function fieldType( value ) { 200 | var type = typeof( value ); 201 | if ( type == 'number'){ 202 | type = ( isInt( value ) ) ? 'integer' : 'float'; 203 | } 204 | return fieldTypes[ type ]; 205 | }; 206 | 207 | // is the value an integer? 208 | function isInt( v ){ 209 | return Math.round( v ) == v; 210 | }; 211 | 212 | function getFields(props, idField) { 213 | var self = this; 214 | var fields = []; 215 | 216 | Object.keys(props).forEach(function(key) { 217 | var type = (idField && key == idField)?'esriFieldTypeOID':fieldType(props[key]); 218 | fields.push({ 219 | name: key.toLowerCase(), 220 | type: type, 221 | alias: key 222 | }); 223 | }); 224 | 225 | return fields; 226 | }; 227 | 228 | function getMaxType(f1, f2) { 229 | var t1 = f1.type, 230 | t2 = f2.type; 231 | if (t1 === t2) return t1; 232 | var c1 = fieldTypePriorities[t1], 233 | c2 = fieldTypePriorities[t2]; 234 | if (c1 && c1.indexOf(t2) > -1) { 235 | return t1; 236 | } else if (c2 && c2.indexOf(t1) > -1) { 237 | return t2; 238 | } else { 239 | throw "***There is no winner defined between " + t1 + " and " + t2 + "!!!"; 240 | } 241 | } 242 | 243 | function calculateFields(geoJSONItem, fields, idField, nameField) { 244 | if (typeof fields === "undefined" || fields===null) { 245 | fields = {}; 246 | } 247 | if (typeof idField === "undefined" || idField===null) { 248 | idField = []; 249 | } 250 | if (typeof nameField === "undefined" || nameField===null) { 251 | nameField = []; 252 | } 253 | 254 | if (geoJSONItem.type === "FeatureCollection") { 255 | for (var i=0; i < geoJSONItem.features.length; i++) { 256 | calculateFields(geoJSONItem.features[i], fields, idField, nameField); 257 | } 258 | } else if (geoJSONItem.type === "Feature") { 259 | if (geoJSONItem.properties) { 260 | var itemFields = getFields(geoJSONItem.properties, idField); 261 | if (idField.length == 0) { 262 | var bestId = defaultIdFields.length; 263 | for (var i=0; i -1 && idIndex < bestId && 266 | validIdFieldTypes.indexOf(itemFields[i].type) > -1) { 267 | bestId = idIndex; 268 | idField.push(itemFields[i]); 269 | } 270 | } 271 | } 272 | for (var i=0; i < itemFields.length; i++) { 273 | if (nameField.length == 0 && itemFields[i].type === "esriFieldTypeString") { 274 | nameField.push(itemFields[i]); 275 | } 276 | var k = itemFields[i].name; // + "_" + itemFields[i].type; 277 | if (!fields.hasOwnProperty(k)) { 278 | fields[k] = itemFields[i]; 279 | } else { 280 | if (fields[k].type !== itemFields[i].type) { 281 | fields[k].type = getMaxType(fields[k], itemFields[i]); 282 | } 283 | } 284 | } 285 | 286 | if (idField.length == 0 && !fields.hasOwnProperty("id")) { 287 | fields["id"] = { 288 | name: 'id', 289 | type: 'esriFieldTypeOID', 290 | alias: 'id' 291 | }; 292 | idField.push(fields["id"]); 293 | } 294 | 295 | if (nameField.length == 0) { 296 | nameField.push(idField[0]); 297 | } 298 | } 299 | } 300 | 301 | var outFields = []; 302 | for (var fieldName in fields) { 303 | outFields.push(fields[fieldName]); 304 | } 305 | 306 | return outFields; 307 | } 308 | 309 | function inflateFields(geoJSONItem, fields) { 310 | if (geoJSONItem.type === "FeatureCollection") { 311 | for (var i=0; i < geoJSONItem.features.length; i++) { 312 | inflateFields(geoJSONItem.features[i], fields); 313 | } 314 | } else if (geoJSONItem.type === "Feature") { 315 | if (geoJSONItem.hasOwnProperty("properties")) { 316 | for (var i=0; i < fields.length; i++) { 317 | var f = fields[i]; 318 | if (!geoJSONItem.properties.hasOwnProperty(f.name)) { 319 | var newVal = null; 320 | if (geoJSONItem.properties.hasOwnProperty(f.alias)) { 321 | newVal = geoJSONItem.properties[f.alias]; 322 | delete geoJSONItem.properties[f.alias]; 323 | } 324 | geoJSONItem.properties[fields[i].name] = newVal; 325 | } 326 | } 327 | } 328 | } 329 | } 330 | 331 | function outputArcGISJSON(geoJSONOutput, cache, query, callback) { 332 | var typeInfo = calculateTypeInfo(geoJSONOutput, query); 333 | 334 | if (typeInfo.types.indexOf("GeometryCollection") > -1) { 335 | console.log("WARNING!!!!! The geoJSON contains at least one GeometryCollection object which Esri GeoServices JSON cannot represent."); 336 | } 337 | 338 | if (typeInfo.type === "GeometryCollection") { 339 | var eString = "The requested or calculated geoJSON type to be returned is GeometryCollection. This is unsupported by Esri GeosServices JSON. Use the optional geoJSONType query parameter to override." 340 | console.log("ERROR!!!!!" + eString); 341 | callback([], null, null, eString); 342 | return; 343 | } 344 | 345 | console.log("Filtering by geoJSON type: " + typeInfo.type + " / " + 346 | typeInfo.types.toString()); 347 | var filteredGeoJSON = FilterGeoJSONByType(geoJSONOutput, typeInfo.type); 348 | 349 | inflateFields(filteredGeoJSON, cache.layerDetails.fields); 350 | 351 | var arcgisOutput = TerraformerArcGIS.convert(filteredGeoJSON); 352 | 353 | if (query.geohubParams.geoJSONType) { 354 | query.outputGeometryType = getEsriGeometryType(query.geohubParams.geoJSONType); 355 | } 356 | 357 | var idField = cache.layerDetails.idField; 358 | 359 | for (var i = 0; i < arcgisOutput.length; i++) { 360 | if (!arcgisOutput[i].hasOwnProperty("attributes")) { 361 | arcgisOutput[i].attributes = {} 362 | } 363 | 364 | var newId = i; 365 | if (arcgisOutput[i].hasOwnProperty("geojsonid")) { 366 | newId = arcgisOutput[i].geojsonid; 367 | arcgisOutput[i].attributes[idField] = newId; 368 | delete arcgisOutput[i].geojsonid; 369 | } else { 370 | console.log("Having to set id on geoJSON with AUTO: " + newId); 371 | } 372 | } 373 | callback(arcgisOutput, idField, cache.layerDetails.fields, null); 374 | } 375 | 376 | 377 | 378 | // This node.js helper function allows us to inherit from dataproviderbase. 379 | util.inherits(GeoHubProvider, dataproviderbase.DataProviderBase); 380 | 381 | // And now we'll override only what we need to (see also /src/dataproviderbase.js). 382 | Object.defineProperties(GeoHubProvider.prototype, { 383 | name: { 384 | get: function() { 385 | // Override the service name - every data provider should override this. 386 | return "geohub"; 387 | } 388 | }, 389 | serverVersion: { 390 | get: function() { 391 | return 10.0; 392 | } 393 | }, 394 | getServiceIds: { 395 | value: function(callback) { 396 | // Override the service name - every data provider should override this. 397 | callback(Object.keys(this._services), null); 398 | } 399 | }, 400 | getServiceName: { 401 | value: function(serviceId) { 402 | var c = parseServiceId(serviceId); 403 | return c.name; 404 | } 405 | }, 406 | getLayerIds: { 407 | value: function(serviceId, callback) { 408 | var c = parseServiceId(serviceId); 409 | serviceId = c.serviceId; 410 | switch (serviceId) { 411 | case "repo": 412 | callback([0], null); 413 | break; 414 | case "gist": 415 | if (c.gistId) { 416 | Geohub.gist({ id: c.gistId }, function(err, geoJSONData) { 417 | if (err) { 418 | console.log(err); 419 | callback([0], err); 420 | } else { 421 | var ids = []; 422 | for (var i=0; i geoJSONData.length) { 782 | console.log("LayerID Out of Range: 0..." + geoJSONData.length); 783 | callback("LayerID Out of Range: 0..." + geoJSONData.length); 784 | return; 785 | } 786 | var cachesLoading = geoJSONData.length; 787 | console.log("LOADING " + cachesLoading + " CACHES"); 788 | var cachesToLoad = []; 789 | for (var i=0; i