Building a custom
AMD module
Use Case:
Real-time earthquake heatmap
Custom Classes
Stability
Scalability
Re-usability
Productivity!
https://pixabay.com/en/recycle-bin-container-recycling-24543/
Image CC0 Public Domain https://pixabay.com/en/spaghetti-bacon-pasta-894077/
require(["esri/map", "dojo/domReady!"], function(Map) {
map = new Map("map", {
basemap: "topo",
center: [-122.45, 37.75],
zoom: 13
});
});
AMD modules...woohoo!
AMD Modules
Written in JavaScript
Logical subsets of functionality
Protect internal content
Portable/Reusable
TO-DO List
Retrieve the GeoJSON data
Convert GeoJSON to Esri JSON array
Convert Esri JSON to Esri.Graphic
Inject array into a FeatureLayer
Apply HeatmapRenderer
https://pixabay.com/en/memo-note-yellow-post-is-670689/
Our go-to tools...
AMD Modules
Custom Classes
Web Workers
Promises
https://pixabay.com/en/army-blade-compact-cut-equipment-2186/
var EarthquakeLayer = declare(null, {
// Custom properties and methods
});
Anonymous Class
SuperClass(es)
Step 1 of 9
Create our Class file
EarthquakeLayer.js
Step 2 of 9
Define EarthquakeLayer Module
define([
"dojo/_base/declare",
"esri/request"
], function(declare, esriRequest) {
return declare(null, {
constructor: function(options){
}
}
)
});
define([
"dojo/_base/declare",
"esri/request"
], function(declare, esriRequest) {
return declare(null, {
constructor: function(options){
}
}
)
});
_base/declare to define a Class
define([
"dojo/_base/declare",
"esri/request"
], function(declare, esriRequest) {
return declare(null, {
constructor: function(options){
}
}
)
});
Create a constructor
define([
"dojo/_base/declare",
"esri/request"
], function(declare, esriRequest) {
return declare(null, {
constructor: function(options){
}
}
)
});
Include esriRequest for retrieving GeoJSON
Create a constructor options Object
define([
"dojo/_base/declare",
"esri/request"
], function(declare, esriRequest) {
return declare(null, {
constructor: function(options){
}
}
)
});
Anonymous Class
define([
"dojo/_base/declare",
"esri/request"
], function(declare, esriRequest) {
return declare(null, {
url: null,
constructor: function(options){
this.url = options.url;
}
})
});
Add a url property for GeoJSON feed
getGeoJSON: function(){
var geoJSON = esriRequest({
"url" : this.url
});
return geoJSON;
}
Create getGeoJSON()
Retrieve our GeoJSON
Step 3 of 9
Create parseFeatures()
Avoid jank!
parseFeatures: function(featureArray){
var parseFeaturesDeferred = new Deferred();
var worker = new Worker("libs/EarthquakeWorker.js");
return parseFeaturesDeferred.promise;
}
Step 4 of 9
Post/receive messages from worker
worker.postMessage(
{
cmd: "parse",
features: featureArray.features
}
);
worker.onmessage = function(result){
worker.terminate();
};
worker.onerror = function(err){
console.log("Worker error: " + err.message);
};
post GeoJSON
receive esriJSON
A few notes
Worker + JSON size sweetspot
- Serialization/deserialization
- Earthquake GeoJSON ~ < 10 MBs
- Larger JSON files may block UI when passed to a Worker
Avoid JSON.parse()
Create Graphics from esriJSON
new Graphic(json)
worker.onmessage = function(result){
worker.terminate();
var graphicsArr = [];
for(var i = 0; i < result.data.length; i++){
var graphic = new Graphic(result.data[i]);
graphicsArr.push(graphic);
}
parseFeaturesDeferred.resolve(graphicsArr);
};
createFeatureLayer()
this.layerDefinition = {
"objectIdField": "id",
"geometryType" : "esriGeometryPoint",
"fields":[{
"name" : "id",
"alias" : "id",
"type" : "esriFieldTypeString"
},{
"name" : "depth",
"alias" : "depth",
"type": "esriFieldTypeInteger"
},{
"name" : "magnitude",
"alias" : "magnitude",
"type": "esriFieldTypeDouble"
}]
};
var featureCollection = {
"layerDefinition": this.layerDefinition,
"featureSet": {
"features": graphicsArr,
"geometryType": "esriGeometryPoint"
}
};
Step 5 of 9
Create feature layer from feature collection
Apply HeatmapRenderer
try {
var featureLayer = new FeatureLayer(featureCollection);
var heatMapRenderer = new HeatmapRenderer({
field: "magnitude",
maxPixelIntensity: 250,
minPixelIntensity: 10,
blurRadius: 10
});
featureLayer.setRenderer(heatMapRenderer);
createFeatureLayerDeferred.resolve(featureLayer);
}
catch(err){
createFeatureLayerDeferred.resolve(false);
}
Create init()
Chain everything together
init: function(){
var dfd = new Deferred();
this.getGeoJSON()
.then(this.parseFeatures)
.then(this.createFeatureLayer.bind(this))
.then(function(result){
dfd.resolve(result);
});
return dfd.promise;
},
Step 6 of 9
Create our web worker
EarthquakeWorker.js
Step 7 of 9
Receive GeoJSON (message.data.features)
Parse the features
Post Esri JSON graphics array
onmessage = function(message) {
switch(message.data.cmd) {
case "parse":
var graphicsArr = parseFeaturesBackground(message.data.features);
postMessage(graphicsArr);
break;
}
};
createParseFeatures()
var graphicsArray = [];
for(var i = 0; i < features.length; i++){
try {
var graphicJson = {
"geometry":{
"x":features[i].geometry.coordinates[0],
"y":features[i].geometry.coordinates[1],
"spatialReference":{"wkid":4326}
},
"attributes":{
"id" : features[i].id,
"depth" : features[i].geometry.coordinates[2],
"magnitude" : features[i].properties.mag
}
};
graphicsArray.push(graphicJson);
}
catch(error){
console.log("Error creating graphic: " + error);
}
}
return graphicsArray;
Step 8 of 9
In index.html create new EarthquakeLayer
map = new Map("map", {
basemap: "topo",
center: [-122.45, 37.75],
zoom: 3
});
esriConfig.defaults.io.corsEnabledServers.push("earthquake.usgs.gov");
var earthquakeLayer = new EarthquakeLayer({
url: "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson"
});
earthquakeLayer.init().then(function(layer){
map.addLayer(layer);
});
Step 9 of 9
https://pixabay.com/en/graduate-graduation-school-student-150373/
https://pixabay.com/en/new-year-new-year-s-day-fireworks-152044/
Possible future enhancements
- Move earthquake retrieval to background thread
- Re-use a single webworker for parsing
- Make heat map properties public
- Include configurable, web-worker background timer to auto-retrieve feed
Additional Resources
Andy Gup
agup@esri.com
@agup
John Gravois
jgravois@esri.com
@geogangster
Advanced Techniques - ArcGIS API for JavaScript
By Andy G
Advanced Techniques - ArcGIS API for JavaScript
- 1,852