widgets/ZoomToFeature.js
define([
'dojo/_base/declare',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
'dijit/_WidgetsInTemplateMixin',
'dojo/_base/lang',
'dojo/_base/array',
'dojo/on',
'dojo/io-query',
'dojo/keys',
'dojo/store/Memory',
'esri/tasks/QueryTask',
'esri/tasks/query',
'esri/layers/GraphicsLayer',
'esri/graphic',
'esri/renderers/SimpleRenderer',
'esri/symbols/SimpleMarkerSymbol',
'esri/symbols/SimpleLineSymbol',
'esri/symbols/SimpleFillSymbol',
'esri/graphicsUtils',
'dojo/text!./ZoomToFeature/templates/ZoomToFeature.html',
'dojo/i18n!./ZoomToFeature/nls/resources',
'dijit/form/Form',
'dijit/form/FilteringSelect',
'xstyle/css!./ZoomToFeature/css/ZoomToFeature.css'
], function (
declare,
_WidgetBase,
_TemplatedMixin,
_WidgetsInTemplateMixin,
lang,
array,
on,
ioQuery,
keys,
Memory,
QueryTask,
Query,
GraphicsLayer,
Graphic,
SimpleRenderer,
SimpleMarkerSymbol,
SimpleLineSymbol,
SimpleFillSymbol,
graphicsUtils,
template,
i18n
) {
return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
widgetsInTemplate: true,
templateString: template,
defaultI18n: i18n,
i18n: {},
baseClass: 'cmwZoomToFeatureWidget',
// url of the MapServer to Query
url: null,
// description field for display in drop-down list
field: null,
// where clause to filter the results
where: '1=1',
// maximum allowable offset to be used for generalizing geometries returned by the query operation
maxAllowableOffset: null,
// Spatial Reference. uses the map's spatial reference if none provided
spatialReference: null,
// Use 0.0001 for decimal degrees (wkid 4326)
// or 500 for meters/feet
pointExtentSize: null,
// default symbology for found features
defaultSymbols: {
point: {
type: 'esriSMS',
style: 'esriSMSCircle',
size: 25,
color: [0, 255, 255, 32],
angle: 0,
xoffset: 0,
yoffset: 0,
outline: {
type: 'esriSLS',
style: 'esriSLSSolid',
color: [0, 255, 255, 255],
width: 2
}
},
polyline: {
type: 'esriSLS',
style: 'esriSLSSolid',
color: [0, 255, 255, 255],
width: 2
},
polygon: {
type: 'esriSFS',
style: 'esriSFSSolid',
color: [0, 255, 255, 32],
outline: {
type: 'esriSLS',
style: 'esriSLSSolid',
color: [0, 255, 255, 255],
width: 2
}
}
},
features: null,
featureStore: null,
featureIdx: null,
postMixInProperties: function () {
this.inherited(arguments);
this.i18n = this.mixinDeep(this.defaultI18n, this.i18n);
},
postCreate: function () {
this.inherited(arguments);
if (!this.spatialReference) {
this.spatialReference = this.map.spatialReference.wkid;
}
if (!this.pointExtentSize) {
if (this.spatialReference === 4326) { // special case for geographic lat/lng
this.pointExtentSize = 0.0001;
} else {
this.pointExtentSize = 250; // could be feet or meters
}
}
this.createGraphicLayers();
this.createGraphicRenderers();
// allow pressing enter key to initiate the search
this.own(on(this.featureSelectDijit, 'keyup', lang.hitch(this, function (evt) {
if (evt.keyCode === keys.ENTER) {
this.search();
}
})));
if (this.url) {
this.getFeatures();
}
},
createGraphicRenderers: function () {
var pointSymbol = null,
polylineSymbol = null,
polygonSymbol = null,
pointRenderer = null,
polylineRenderer = null,
polygonRenderer = null;
var symbols = lang.mixin({}, this.symbols);
// handle each property to preserve as much of the object heirarchy as possible
symbols = {
point: lang.mixin(this.defaultSymbols.point, symbols.point),
polyline: lang.mixin(this.defaultSymbols.polyline, symbols.polyline),
polygon: lang.mixin(this.defaultSymbols.polygon, symbols.polygon)
};
if (symbols.point && this.pointGraphics) {
pointSymbol = new SimpleMarkerSymbol(symbols.point);
pointRenderer = new SimpleRenderer(pointSymbol);
pointRenderer.label = 'Search Results (Points)';
pointRenderer.description = 'Search results (Points)';
this.pointGraphics.setRenderer(pointRenderer);
}
if (symbols.polyline && this.polylineGraphics) {
polylineSymbol = new SimpleLineSymbol(symbols.polyline);
polylineRenderer = new SimpleRenderer(polylineSymbol);
polylineRenderer.label = 'Search Results (Lines)';
polylineRenderer.description = 'Search Results (Lines)';
this.polylineGraphics.setRenderer(polylineRenderer);
}
if (symbols.polygon && this.polygonGraphics) {
polygonSymbol = new SimpleFillSymbol(symbols.polygon);
polygonRenderer = new SimpleRenderer(polygonSymbol);
polygonRenderer.label = 'Search Results (Polygons)';
polygonRenderer.description = 'Search Results (Polygons)';
this.polygonGraphics.setRenderer(polygonRenderer);
}
},
createGraphicLayers: function () {
// points
this.pointGraphics = new GraphicsLayer({
id: this.id + '_Points',
title: this.id + ' Points'
});
// polyline
this.polylineGraphics = new GraphicsLayer({
id: this.id + '_Lines',
title: this.id + ' Lines'
});
// polygons
this.polygonGraphics = new GraphicsLayer({
id: this.id + '_Polygons',
title: this.id + ' Polygons'
});
this.map.addLayer(this.polygonGraphics);
this.map.addLayer(this.polylineGraphics);
this.map.addLayer(this.pointGraphics);
},
getFeatures: function () {
var query = new Query();
query.outFields = [this.field];
query.orderByFields = [this.field];
query.where = this.where;
query.returnGeometry = true;
if (this.maxAllowableOffset) {
query.maxAllowableOffset = this.maxAllowableOffset;
}
//query.returnDistinctValues = true;
query.outSpatialReference = {
wkid: this.spatialReference
};
var queryTask = new QueryTask(this.url);
queryTask.execute(query, lang.hitch(this, 'populateList'));
},
//Populate the dropdown list box with unique values
populateList: function (results) {
this.features = results.features;
var values = [],
k = 0,
field = this.field,
idx = -1;
var uri = window.location.href;
var qs = uri.substring(uri.indexOf('?') + 1, uri.length);
var qsObj = ioQuery.queryToObject(qs);
var value = qsObj[field] || '';
// if there is only one feature, use that to zoom immediately
if (this.features.length === 1) {
idx = 0;
}
array.forEach(this.features, function (feature) {
var name = feature.attributes[field];
if (name === value) {
idx = k;
}
values.push({
id: k,
name: name
});
k++;
});
this.featureStore = new Memory({
data: values
});
this.featureSelectDijit.set('store', this.featureStore);
this.featureSelectDijit.set('disabled', false);
if (idx >= 0) {
this.featureSelectDijit.set('value', idx);
}
},
onFeatureChange: function (featureIdx) {
if (featureIdx >= 0 && featureIdx < this.features.length) {
this.featureIdx = featureIdx;
this.search();
}
},
search: function () {
this.clearFeatures();
if (this.featureIdx === null) {
return;
}
var feature = this.features[this.featureIdx];
if (feature) {
this.highlightFeature(feature);
var extent = this.getGraphicsExtent([feature]);
if (extent) {
this.zoomToExtent(extent);
}
}
},
clearResults: function () {
this.featureIdx = null;
this.clearFeatures();
this.clearButtonDijit.set('disabled', true);
this.featureSelectDijit.reset();
},
clearFeatures: function () {
this.pointGraphics.clear();
this.polylineGraphics.clear();
this.polygonGraphics.clear();
},
highlightFeature: function (feature) {
var graphic = null;
switch (feature.geometry.type) {
case 'point':
// only add points to the map that have an X/Y
if (feature.geometry.x && feature.geometry.y) {
graphic = new Graphic(feature.geometry);
this.pointGraphics.add(graphic);
this.clearButtonDijit.set('disabled', false);
}
break;
case 'polyline':
// only add polylines to the map that have paths
if (feature.geometry.paths && feature.geometry.paths.length > 0) {
graphic = new Graphic(feature.geometry);
this.polylineGraphics.add(graphic);
this.clearButtonDijit.set('disabled', false);
}
break;
case 'polygon':
// only add polygons to the map that have rings
if (feature.geometry.rings && feature.geometry.rings.length > 0) {
graphic = new Graphic(feature.geometry, null, {
ren: 1
});
this.polygonGraphics.add(graphic);
this.clearButtonDijit.set('disabled', false);
}
break;
default:
break;
}
},
zoomToExtent: function (extent) {
this.map.setExtent(extent.expand(1.5));
},
getGraphicsExtent: function (graphics) {
var extent = null;
if (graphics && graphics.length > 0) {
extent = graphicsUtils.graphicsExtent(graphics);
if (extent.xmin === extent.xmax || extent.ymin === extent.ymax) {
extent = this.expandExtent(extent);
}
}
return extent;
},
expandExtent: function (extent) {
extent.xmin -= this.pointExtentSize;
extent.ymin -= this.pointExtentSize;
extent.xmax += this.pointExtentSize;
extent.ymax += this.pointExtentSize;
return extent;
},
mixinDeep: function (dest, source) {
//Recursively mix the properties of two objects
var empty = {};
for (var name in source) {
if (!(name in dest) || (dest[name] !== source[name] && (!(name in empty) || empty[name] !== source[name]))) {
try {
if (source[name].constructor === Object) {
dest[name] = this.mixinDeep(dest[name], source[name]);
} else {
dest[name] = source[name];
}
} catch (e) {
// Property in destination object not set. Create it and set its value.
dest[name] = source[name];
}
}
}
return dest;
}
});
});