├── .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 | Virtual Services - Top-level REST Endpoints
13 |
14 |
15 |
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 | ArcGIS REST Services Directory
12 |
13 |
14 |
24 |
25 |
26 |
27 | JSON
28 |
29 |
30 |
31 |
34 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/resources/templates/services.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Folder: /
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ArcGIS REST Services Directory
13 |
14 |
15 |
25 |
26 |
27 |
28 | JSON
29 |
30 |
31 |
32 |
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 | ArcGIS REST Services Directory
13 |
14 |
15 |
25 |
26 |
27 |
28 | JSON
29 |
30 |
31 |
32 |
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 |
45 | Is Token Based Security : false
46 |
47 |
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 | Back to World View
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 | ArcGIS REST Services Directory
12 |
13 |
14 |
24 |
25 |
26 |
27 | JSON
28 |
29 |
30 |
31 |
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 | ArcGIS REST Services Directory
13 |
14 |
15 |
25 |
26 |
27 |
28 | JSON
29 |
30 |
31 |
32 |
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