googlemaps-v3-utility-library
Version:
Google Maps API V3 Utility Library
3,790 lines • 145 kB
JavaScript
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* @preserve http://google-maps-utility-library-v3.googlecode.com
*/
/**
* @name ArcGIS Server Link for Google Maps JavaScript API V3
* @version 1.0
* @author: Nianwei Liu (nianwei at gmail dot com)
* @fileoverview
* <p><a href="examples.html">Examples</a>
* </p>
* <p>This library lets you add map resources accessible via
* <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/index.html'>
* ESRI ArcGIS Server™ REST API</a> into <a
* href='http://code.google.com/apis/maps/documentation/javascript/'>
* Google Maps API V3</a> and provide some additional support for map tiles created
* with different spatial reference and tiling scheme.</p>
* </p>.
* <table>
* <tr>
* <td style = 'width:200px'>
* {@link TileLayer}<br/>
* {@link TileLayerOptions}<br/>
* {@link MapType}<br/>
* {@link MapTypeOptions}<br/>
* {@link MapOverlay}<br/>
* {@link MapOverlayOptions}<br/>
* {@link Projection}<br/>
* </td>
* <td style = 'width:200px'>
* {@link Catalog}<br/>
* {@link MapService}<br/></b>
* {@link Layer}<br/>
* {@link GeocodeService}<br/>
* {@link GeometryService}<br/>
* {@link GPService}<br/>
* {@link GPTask}<br/>
* {@link RouteTask}<br/>
* <br/></td>
* <td style = 'width:200px'>
* {@link SpatialReference}<br/>
* {@link Geographic}<br/>
* {@link LambertConformalConic}<br/>
* {@link TransverseMercator}<br/>
* {@link SphereMercator}<br/>
* {@link Albers}<br/>
* {@link SpatialRelationship}<br/>
* </td>
* <td style = 'width:200px'>
* {@link Util} <br/>
* {@link Config} <br/>
* {@link Error} <br/>
* </td>
* </tr></table>
* <p> There are many objects used in the REST API that do not require
* a constructor and can be
* used just as object literal in the operation:</p>
* <table><tr>
* <td style = 'width:200px'>
* {@link Field}<br/>
* {@link TileInfo}<br/>
* {@link LOD}<br/>
* {@link ExportMapOptions}<br/>
* {@link MapImage}<br/>
* {@link IdentifyOptions}<br/>
* {@link IdentifyResults}<br/>
* {@link IdentifyResult}<br/>
* <br/></td>
* <td style = 'width:200px'>
* {@link QueryOptions}<br/>
* {@link ResultSet}<br/>
* {@link FindOptions}<br/>
* {@link FindResults}<br/>
* {@link FindResult}<br/>
* {@link Feature}<br/>
* </td>
* <td style = 'width:200px'>
* {@link GeocodeOptions}<br/>
* {@link GeocodeResults}<br/>
* {@link GeocodeResult}<br/>
* {@link ReverseGeocodeOptions}<br/>
* {@link ReverseGeocodeResult}<br/>
* {@link BufferOptions}<br/>
* {@link BufferResults}<br/>
* {@link ProjectOptions}<br/>
* {@link ProjectResults}<br/>
* </td>
* <td style = 'width:200px'>
* {@link RouteOptions}<br/>
* {@link RouteResults}<br/>
* </td>
* </tr></table>
*/
/*jslint evil: true, sub: true */
/*global escape ActiveXObject */
var gmaps = gmaps || {};
/** @const */
var RAD_DEG = Math.PI / 180;
var jsonpID_ = 0;
window['ags_jsonp'] = window['ags_jsonp'] || {};
var G = google.maps;
var WGS84, NAD83, WEB_MERCATOR, WEB_MERCATOR_AUX;
/**
* @name Config
* @class This is an object literal that sets common configuration values used across the lib.
* @property {String} [proxyUrl] The URL to the web proxy page used in case the length of the URL request to an ArcGIS Server REST resource exceeds 2000 characters.
* @property {Boolean} [alwaysUseProxy] whether to always use proxy page when send request to server.
*/
var Config = {
proxyUrl:null,
alwaysUseProxy: false
};
/**
* an internal collection of Spatial Refeneces supported in the application.
* The key of the collection is the wkid/wkt, and value is an instance of
* {@link SpatialReference}.
*/
var spatialReferences_ = {};
/**
* A set of utilities ((<code>Util</code>)
* for commonly used functions.
* @name Util
* @namespace
*/
var Util = {};
/**
* Extract the substring from full string, between start string and end string
* @param {String} full
* @param {String} start
* @param {String} end
*/
function extractString_(full, start, end) {
var i = (start === '') ? 0 : full.indexOf(start);
var e = end === '' ? full.length : full.indexOf(end, i + start.length);
return full.substring(i + start.length, e);
}
/**
* Check if the object is String
* @param {Object} o
*/
function isString_(o) {
return o && typeof o === 'string';
}
/**
* Check if the object is array
* @param {Object} o
*/
function isArray_(o) {
return o && o.splice;
}
function isNumber_(o) {
return typeof o === 'number';
}
/**
* Add the property of the source object to destination object
* if not already exists.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} force
* @return {Object}
*/
function augmentObject_(src, dest, force) {
if (src && dest) {
var p;
for (p in src) {
if (force || !(p in dest)) {
dest[p] = src[p];
}
}
}
return dest;
}
/**
* Wrapper around google.maps.event.trigger
* @param {Object} src
* @param {String} evtName
* @param {Object} args
*/
function triggerEvent_(src, evtName, args) {
G.event.trigger.apply(this, arguments);
}
/**
* handle JSON error
* @param {Object} errback
* @param {Object} json
*/
function handleErr_(errback, json) {
if (errback && json && json.error) {
errback(json.error);
}
}
/**
* get REST format for 2 time
* @param {Date} time
* @param {Date} endTime
*/
function formatTimeString_(time, endTime) {
var ret = '';
if (time) {
ret += (time.getTime() - time.getTimezoneOffset() * 60000);
}
if (endTime) {
ret += ', ' + (endTime.getTime() - endTime.getTimezoneOffset() * 60000);
}
return ret;
}
/**
* Set opacity of a node.
* @param {Node} node
* @param {Number} 0-1
*/
function setNodeOpacity_(node, op) {
// closure compiler removed?
op = Math.min(Math.max(op, 0), 1);
if (node) {
var st = node.style;
if (typeof st.opacity !== 'undefined') {
st.opacity = op;
}
if (typeof st.filters !== 'undefined') {
st.filters.alpha.opacity = Math.floor(100 * op);
}
if (typeof st.filter !== 'undefined') {
st.filter = "alpha(opacity:" + Math.floor(op * 100) + ")";
}
}
}
/**
* get the layerdef text string from an object literal
* @param {Object} defs
*/
function getLayerDefsString_(defs) {
var strDefs = '';
for (var x in defs) {
if (defs.hasOwnProperty(x)) {
if (strDefs.length > 0) {
strDefs += ';';
}
strDefs += (x + ':' + defs[x]);
}
}
return strDefs;
}
function getXmlHttp_() {
if (typeof XMLHttpRequest === "undefined") {
try {
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
} catch (e) {
}
try {
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
} catch (e1) {
}
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} catch (e2) {
}
throw new Error("This browser does not support XMLHttpRequest.");
} else {
return new XMLHttpRequest();
}
}
/**
* @name GeometryType
* @enum {String}
* @const
* @class List of Geometry type supported by ArcGIS server.
* @property {String} [POINT] esriGeometryPoint
* @property {String} [MULTIPOINT] esriGeometryMultipoint
* @property {String} [POLYLINE] esriGeometryPolyline
* @property {String} [POLYGON] esriGeometryPolygon
* @property {String} [ENVELOPE] esriGeometryEnvelope
*/
var GeometryType = {
POINT: 'esriGeometryPoint',
MULTIPOINT: 'esriGeometryMultipoint',
POLYLINE: 'esriGeometryPolyline',
POLYGON: 'esriGeometryPolygon',
ENVELOPE: 'esriGeometryEnvelope'
};
function getGeometryType_(obj) {
var o = obj;
if (isArray_(obj) && obj.length > 0) {
o = obj[0];
}
if (o instanceof G.LatLng || o instanceof G.Marker) {
if (isArray_(obj) && obj.length > 1) {
return GeometryType.MULTIPOINT;
} else {
return GeometryType.POINT;
}
} else if (o instanceof G.Polyline) {
return GeometryType.POLYLINE;
} else if (o instanceof G.Polygon) {
return GeometryType.POLYGON;
} else if (o instanceof G.LatLngBounds) {
return GeometryType.ENVELOPE;
} else if (o.x !== undefined && o.y !== undefined) {
return GeometryType.POINT;
} else if (o.points) {
return GeometryType.MULTIPOINT;
} else if (o.paths) {
return GeometryType.POLYLINE;
} else if (o.rings) {
return GeometryType.POLYGON;
}
return null;
}
/**
* Is the object an Google Overlay?
* @param {Object} obj
* @return {Boolean}
*/
function isOverlay_(obj) {
var o = obj;
if (isArray_(obj) && obj.length > 0) {
o = obj[0];
}
if (isArray_(o) && o.length > 0) {
o = o[0];
}
if (o instanceof G.LatLng || o instanceof G.Marker ||
o instanceof G.Polyline ||
o instanceof G.Polygon ||
o instanceof G.LatLngBounds) {
return true;
}
return false;
}
function formatSRParam_(sr) {
if (!sr) {
return null;
}
// for 9.3 compatibility, return wkid if possible.
return isNumber_(sr) ? sr : sr.wkid ? sr.wkid : sr.toJSON();
}
/**
* @param {MVCArrayOfLatLng} pts
*/
function fromLatLngsToJSON_(pts, close) {
var arr = [];
var latlng;
for (var i = 0, c = pts.getLength(); i < c; i++) {
latlng = pts.getAt(i);
arr.push('[' + latlng.lng() + ',' + latlng.lat() + ']');
}
if (close && arr.length > 0) {
arr.push('[' + pts.getAt(0).lng() + ',' + pts.getAt(0).lat() + ']');
}
return arr.join(',');
}
/**
* Convert overlays (Marker, Polyline, Polygons) to JSON string in AGS format.
* @param {OverlayView|Array.OverlayView} geom
*/
function fromOverlaysToJSON_(geom) {
var gtype = getGeometryType_(geom);
var g, gs, i, pts;
var json = '{';
switch (gtype) {
case GeometryType.POINT:
g = isArray_(geom) ? geom[0] : geom;
if (g instanceof G.Marker) {
g = g.getPosition();
}
json += 'x:' + g.lng() + ',y:' + g.lat();
break;
case GeometryType.MULTIPOINT:
pts = [];
for (i = 0; i < geom.length; i++) {
if (geom[i] instanceof G.Marker) {
g = geom[i].getPosition();
} else {
g = geom[i];
}
pts.push('[' + g.lng() + ',' + g.lat() + ']');
}
json += 'points: [' + pts.join(',') + ']';
break;
case GeometryType.POLYLINE:
// V3 does not support multiple paths yet
pts = [];
gs = isArray_(geom) ? geom : [geom];
for (i = 0; i < gs.length; i++) {
pts.push('[' + fromLatLngsToJSON_(gs[i].getPath()) + ']');
}
json += 'paths:[' + pts.join(',') + ']';
break;
case GeometryType.POLYGON:
pts = [];
g = isArray_(geom) ? geom[0] : geom;
var paths = g.getPaths();
for (i = 0; i < paths.getLength(); i++) {
pts.push('[' + fromLatLngsToJSON_(paths.getAt(i), true) + ']');
}
json += 'rings:[' + pts.join(',') + ']';
break;
case GeometryType.ENVELOPE:
g = isArray_(geom) ? geom[0] : geom;
json += 'xmin:' + g.getSouthWest().lng() + ',ymin:' + g.getSouthWest().lat() + ',xmax:' + g.getNorthEast().lng() + ',ymax:' + g.getNorthEast().lat();
break;
}
json += ', spatialReference:{wkid:4326}';
json += '}';
return json;
}
/**
* From ESRI geometry format to JSON String, primarily used in Geometry service
* @param {Object} geom
*/
function fromGeometryToJSON_(geom) {
function fromPointsToJSON(pts) {
var arr = [];
for (var i = 0, c = pts.length; i < c; i++) {
arr.push('[' + pts[i][0] + ',' + pts[i][1] + ']');
}
return '[' + arr.join(',') + ']';
}
function fromLinesToJSON(lines) {
var arr = [];
for (var i = 0, c = lines.length; i < c; i++) {
arr.push(fromPointsToJSON(lines[i]));
}
return '[' + arr.join(',') + ']';
}
var json = '{';
if (geom.x) {
json += 'x:' + geom.x + ',y:' + geom.y;
} else if (geom.xmin) {
json += 'xmin:' + geom.xmin + ',ymin:' + geom.ymin + ',xmax:' + geom.xmax + ',ymax:' + geom.ymax;
} else if (geom.points) {
json += 'points:' + fromPointsToJSON(geom.points);
} else if (geom.paths) {
json += 'paths:' + fromLinesToJSON(geom.paths);
} else if (geom.rings) {
json += 'rings:' + fromLinesToJSON(geom.rings);
}
json += '}';
return json;
}
/**
* Helper method to convert an Envelope object to <code>google.maps.LatLngBounds</code>
* @private
* @param {Object} extent
* @return {google.maps.LatLngBounds} gLatLngBounds
*/
function fromEnvelopeToLatLngBounds_(extent) {
var sr = spatialReferences_[extent.spatialReference.wkid || extent.spatialReference.wkt];
sr = sr || WGS84;
var sw = sr.inverse([extent.xmin, extent.ymin]);
var ne = sr.inverse([extent.xmax, extent.ymax]);
return new G.LatLngBounds(new G.LatLng(sw[1], sw[0]), new G.LatLng(ne[1], ne[0]));
}
/**
* Convert a ArcGIS Geometry JSON object to core Google Maps API
* overlays such as <code>google.maps.Marker</code>, <code>google.maps.Polyline</code> or <code>google.maps.Polygon</code>
* Note ArcGIS Geometry may have multiple parts, but the coresponding OverlayView
* may (Polygon) or may not (Polyline) support multi-parts, so the result is an array for consistency.
* @param {Object} json geometry
* @param {OverlayOptions} opts see {@link OverlayOptions}
* @return {Array.OverlayView}
*/
function fromJSONToOverlays_(geom, opts) {
var ovs = null;
var ov;
var i, ic, j, jc, parts, part, lnglat, latlngs;
opts = opts || {};
if (geom) {
ovs = [];
if (geom.x) {
ov = new G.Marker(augmentObject_(opts.markerOptions || opts, {
'position': new G.LatLng(geom.y, geom.x)
}));
ovs.push(ov);
} else {
//mulpt, line and poly
parts = geom.points || geom.paths || geom.rings;
if (!parts) {
return ovs;
}
var rings = [];
for (i = 0, ic = parts.length; i < ic; i++) {
part = parts[i];
if (geom.points) {
// multipoint
ov = new G.Marker(augmentObject_(opts.markerOptions || opts, {
'position': new G.LatLng(part[1], part[0])
}));
ovs.push(ov);
} else {
latlngs = [];
for (j = 0, jc = part.length; j < jc; j++) {
lnglat = part[j];
latlngs.push(new G.LatLng(lnglat[1], lnglat[0]));
}
if (geom.paths) {
ov = new G.Polyline(augmentObject_(opts.polylineOptions || opts, {
'path': latlngs
}));
ovs.push(ov);
} else if (geom.rings) {
// V3 supports multiple rings
rings.push(latlngs);
}
}
}
if (geom.rings) {
ov = new G.Polygon(augmentObject_(opts.polygonOptions || opts, {
'paths': rings
}));
ovs.push(ov);
}
}
}
return ovs;
}
function parseFeatures_(features, ovOpts) {
if (features) {
var i, I, f;
for (i = 0, I = features.length; i < I; i++) {
f = features[i];
if (f.geometry) {
f.geometry = fromJSONToOverlays_(f.geometry, ovOpts);
}
}
}
}
/**
* get string as rest parameter
* @param {Object} o
*/
function formatRequestString_(o) {
var ret;
if (typeof o === 'object') {
if (isArray_(o)) {
ret = [];
for (var i = 0, I = o.length; i < I; i++) {
ret.push(formatRequestString_(o[i]));
}
return '[' + ret.join(',') + ']';
} else if (isOverlay_(o)) {
return fromOverlaysToJSON_(o);
} else if (o.toJSON) {
return o.toJSON();
} else {
ret = '';
for (var x in o) {
if (o.hasOwnProperty(x)) {
if (ret.length > 0) {
ret += ', ';
}
ret += x + ':' + formatRequestString_(o[x]);
}
}
return '{' + ret + '}';
}
}
return o.toString();
}
function fromLatLngsToFeatureSet_(latlngs) {
var i, I, latlng;
var features = [];
for (i = 0, I = latlngs.length; i < I; i++) {
latlng = latlngs[i];
if (latlng instanceof G.Marker) {
latlng = latlng.getPosition();
}
features.push({
'geometry': {
'x': latlng.lng(),
'y': latlng.lat(),
'spatialReference': {
'wkid': 4326
}
}
});
}
return {
'type': '"features"',
'features': features,
'doNotLocateOnRestrictedElements': false
};
}
function prepareGeometryParams_(p) {
var params = {};
if (!p) {
return null;
}
var json = [];
var g, isOv;
if (p.geometries && p.geometries.length > 0) {
g = p.geometries[0];
isOv = isOverlay_(g);
for (var i = 0, c = p.geometries.length; i < c; i++) {
if (isOv) {
json.push(fromOverlaysToJSON_(p.geometries[i]));
} else {
json.push(fromGeometryToJSON_(p.geometries[i]));
}
}
}
if (!p.geometryType) {
p.geometryType = getGeometryType_(g);
}
if (isOv) {
params.inSR = WGS84.wkid;
} else if (p.inSpatialReference) {
params.inSR = formatSRParam_(p.inSpatialReference);
}
if (p.outSpatialReference) {
params.outSR = formatSRParam_(p.outSpatialReference);
}
params.geometries = '{geometryType:"' + p.geometryType + '", geometries:[' + json.join(',') + ']}';
return params;
}
function log_(msg) {
if (window.console) {
window.console.log(msg);
} else {
var l = document.getElementById('_ags_log');
if (l) {
l.innerHTML = l.innerHTML + msg + '<br/>';
}
}
}
/**
* Format params to URL string
* @param {Object} params
*/
function formatParams_(params) {
var query = '';
if (params) {
params.f = params.f || 'json';
for (var x in params) {
if (params.hasOwnProperty(x) && params[x] !== null && params[x] !== undefined) { // wont sent undefined.
//jslint complaint about escape cause NN does not support it.
var val = formatRequestString_(params[x]);
query += (query.length > 0?'&':'')+(x + '=' + (escape ? escape(val) : encodeURIComponent(val)));
}
}
}
return query;
}
/** create a callback closure
* @private
* @param {Object} fn
* @param {Object} obj
*/
function callback_(fn, obj) {
var args = [];
for (var i = 2, c = arguments.length; i < c; i++) {
args.push(arguments[i]);
}
return function() {
fn.apply(obj, args);
};
}
function addCopyrightInfo_(cpArray, mapService, map) {
if (mapService.hasLoaded()) {
cpArray.push(mapService.copyrightText);
} else {
G.event.addListenerOnce(mapService, 'load', function() {
setCopyrightInfo_(map);
});
}
}
/**
* Find copyright control in the map
* @param {Object} map
*/
function setCopyrightInfo_(map) {
var div = null;
if (map) {
var mvc = map.controls[G.ControlPosition.BOTTOM_RIGHT];
if (mvc) {
for (var i = 0, c = mvc.getLength(); i < c; i++) {
if (mvc.getAt(i).id === 'agsCopyrights') {
div = mvc.getAt(i);
break;
}
}
}
//var callback = callback_(setCopyrightInfo_, null, map);
if (!div) {
div = document.createElement('div');
div.style.fontFamily = 'Arial,sans-serif';
div.style.fontSize = '10px';
div.style.textAlign = 'right';
div.id = 'agsCopyrights';
map.controls[G.ControlPosition.BOTTOM_RIGHT].push(div);
G.event.addListener(map, 'maptypeid_changed', function() {
setCopyrightInfo_(map);
});
}
var ovs = map.agsOverlays;
var cp = [];
var svc, type;
if (ovs) {
for (var i = 0, c = ovs.getLength(); i < c; i++) {
addCopyrightInfo_(cp, ovs.getAt(i).mapService_, map);
}
}
var ovTypes = map.overlayMapTypes;
if (ovTypes) {
for (var i = 0, c = ovTypes.getLength(); i < c; i++) {
type = ovTypes.getAt(i);
if (type instanceof MapType) {
for (var j = 0, cj = type.tileLayers_.length; j < cj; j++) {
addCopyrightInfo_(cp, type.tileLayers_[j].mapService_, map);
}
}
}
}
type = map.mapTypes.get(map.getMapTypeId());
if (type instanceof MapType) {
for (var i = 0, c = type.tileLayers_.length; i < c; i++) {
addCopyrightInfo_(cp, type.tileLayers_[i].mapService_, map);
}
if (type.negative) {
div.style.color = '#ffffff';
} else {
div.style.color = '#000000';
}
}
div.innerHTML = cp.join('<br/>');
}
}
function getJSON_(url, params, callbackName, callbackFn) {
var sid = 'ags_jsonp_' + (jsonpID_++) + '_' + Math.floor(Math.random() * 1000000);
var script = null;
params = params || {};
// AGS10.1 escapes && so had to take it off.
params[callbackName || 'callback'] = 'ags_jsonp.' + sid;
var query = formatParams_(params);
var head = document.getElementsByTagName("head")[0];
if (!head) {
throw new Error("document must have header tag");
}
var jsonpcallback = function() {
if (window['ags_jsonp'][sid]) {
delete window['ags_jsonp'][sid]; //['ags_jsonp']
}
if (script) {
head.removeChild(script);
}
script = null;
callbackFn.apply(null, arguments);
/**
* This event is fired after a REST JSONP response was returned by server.
* @name Util#jsonpend
* @param {String} scriptID
* @event
*/
triggerEvent_(Util, 'jsonpend', sid);
};
window['ags_jsonp'][sid] = jsonpcallback;
if ((query + url).length < 2000 && !Config.alwaysUseProxy) {
script = document.createElement("script");
script.src = url + (url.indexOf('?') === -1 ? '?' : '&') + query;
script.id = sid;
head.appendChild(script);
} else {
// check if same host
var loc = window.location;
var dom = loc.protocol + '//' + loc.hostname + (!loc.port || loc.port === 80 ? '' : ':' + loc.port + '/');
var useProxy = true;
if (url.toLowerCase().indexOf(dom.toLowerCase()) !== -1) {
useProxy = false;
}
if (Config.alwaysUseProxy) {
useProxy = true;
}
if (useProxy && !Config.proxyUrl) {
throw new Error('No proxyUrl property in Config is defined');
}
var xmlhttp = getXmlHttp_();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4) {
if (xmlhttp.status === 200) {
eval(xmlhttp.responseText);
} else {
throw new Error("Error code " + xmlhttp.status);
}
}
};
xmlhttp.open('POST', useProxy ? Config.proxyUrl + '?' + url : url, true);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlhttp.send(query);
}
/**
* This event is fired before a REST request sent to server.
* @name Util#jsonpstart
* @param {String} scriptID
* @event
*/
triggerEvent_(Util, 'jsonpstart', sid);
return sid;
}
/**
* Make Cross Domain Calls. This function returns the
* script ID which can be used to track the requests. parameters:
* <ul>
* <li>url: url of server resource
* <li>params: an object with name,value pairs. value must be string
* <li>callbackName: Callback parameter name the server is expecting.e.g:'callback'
* <li>callbackFn: the actual callback function.
* </ul>
* @param {String} url
* @param {Object} params
* @param {String} callbackName
* @param {Function} callbackFn
* @return {String} scriptID
*/
Util.getJSON = function(url, params, callbackName, callbackFn) {
getJSON_(url, params, callbackName, callbackFn);
};
/**
* Add a list of overlays to map
* @param {google.maps.Map} map
* @param {Array.OverlayView} overlays
*/
Util.addToMap = function(map, overlays) {
if (isArray_(overlays)) {
var ov;
for (var i = 0, I = overlays.length; i < I; i++) {
ov = overlays[i];
if (isArray_(ov)) {
Util.addToMap(map, ov);
} else if (isOverlay_(ov)) {
ov.setMap(map);
}
}
}
};
/**
* Add a list of overlays to map
* @param {Array.OverlayView} overlays
* @param {Boolean} clearArray
*/
Util.removeFromMap = function(overlays, clearArray) {
Util.addToMap(null, overlays);
if (clearArray) {
overlays.length = 0;
}
};
/**
* Create A Generic Spatial Reference Object
* The <code>params </code> passed in constructor is a javascript object literal and depends on
* the type of Coordinate System to construct.
* @name SpatialReference
* @class This class (<code>SpatialReference</code>) is for coordinate systems that converts value
* between geographic and real-world coordinates. The following classes extend this class:
* {@link Geographic}, {@link SphereMercator}, {@link LambertConformalConic}, and {@link TransverseMercator}.
* @constructor
* @property {Number} [wkid] well-known coodinate system id (EPSG code)
* @property {String} [wkt] well-known coodinate system text
* @param {Object} params
*/
function SpatialReference(params) {
params = params || {};
this.wkid = params.wkid;
this.wkt = params.wkt;
}
/**
* Convert Lat Lng to real-world coordinates.
* Note both input and output are array of [x,y], although their values in different units.
* @param {Array.number} lnglat
* @return {Array.number}
*/
SpatialReference.prototype.forward = function(lnglat) {
return lnglat;
};
/**
* Convert real-world coordinates to Lat Lng.
* Note both input and output are are array of [x,y], although their values are different.
* @param {Array.number} coords
* @return {Array.number}
*/
SpatialReference.prototype.inverse = function(coords) {
return coords;
};
/**
* Get the map the periodicity in x-direction, in map units NOT pixels
* @return {number} periodicity in x-direction
*/
SpatialReference.prototype.getCircum = function() {
return 360;
};
/**
* To JSON String
* @return String
*/
SpatialReference.prototype.toJSON = function() {
return '{' + (this.wkid ? ' wkid:' + this.wkid : 'wkt: \'' + this.wkt + '\'') + '}';
};
/**
* Creates a Geographic Coordinate System. e.g.:<br/>
* <code> var g2 = new Geographic({wkid:4326});
* </code>
* @name Geographic
* @class This class (<code>Geographic</code>) will simply retuns same LatLng as Coordinates.
* The <code>param</code> should have wkid property. Any Geographic Coordinate Systems (eg. WGS84(4326)) can
* use this class As-Is.
* <br/>Note:<b> This class does not support datum transformation</b>.
* @constructor
* @extends SpatialReference
* @param {Object} params
*/
function Geographic (params) {
params = params || {};
SpatialReference.call(this, params);
}
Geographic.prototype = new SpatialReference();
/**
* Create a Lambert Conformal Conic Projection based Spatial Reference. The <code>params</code> passed in construction should
* include the following properties:<code>
* <br/>-wkid: well-known id
* <br/>-semi_major: ellipsoidal semi-major axis in meter
* <br/>-unit: meters per unit
* <br/>-inverse_flattening: inverse of flattening of the ellipsoid where 1/f = a/(a - b)
* <br/>-standard_parallel_1: phi1, latitude of the first standard parallel
* <br/>-standard_parallel_2: phi2, latitude of the second standard parallel
* <br/>-latitude_of_origin: phi0, latitude of the false origin
* <br/>-central_meridian: lamda0, longitude of the false origin (with respect to the prime meridian)
* <br/>-false_easting: FE, false easting, the Eastings value assigned to the natural origin
* <br/>-false_northing: FN, false northing, the Northings value assigned to the natural origin
* </code>
* <br/> e.g. North Carolina State Plane NAD83 Feet: <br/>
* <code> var ncsp82 = new LambertConformalConic({wkid:2264, semi_major: 6378137.0,inverse_flattening: 298.257222101,
* standard_parallel_1: 34.33333333333334, standard_parallel_2: 36.16666666666666,
* central_meridian: -79.0, latitude_of_origin: 33.75,false_easting: 2000000.002616666,
* 'false_northing': 0, unit: 0.3048006096012192 }); </code>
* @name LambertConformalConic
* @class This class (<code>LambertConformalConic</code>) represents a Spatial Reference System based on <a target = wiki href = 'http://en.wikipedia.org/wiki/Lambert_conformal_conic_projection'>Lambert Conformal Conic Projection</a>.
* @extends SpatialReference
* @constructor
* @param {Object} params
*/
function LambertConformalConic(params) {
//http://pubs.er.usgs.gov/djvu/PP/PP_1395.pdf
//for NCSP83: GLatLng(35.102363,-80.5666)< === > GPoint(1531463.95, 495879.744);
params = params || {};
SpatialReference.call(this, params);
var f_i = params.inverse_flattening;
var phi1 = params.standard_parallel_1 * RAD_DEG;
var phi2 = params.standard_parallel_2 * RAD_DEG;
var phi0 = params.latitude_of_origin * RAD_DEG;
this.a_ = params.semi_major / params.unit;
this.lamda0_ = params.central_meridian * RAD_DEG;
this.FE_ = params.false_easting;
this.FN_ = params.false_northing;
var f = 1.0 / f_i; //e: eccentricity of the ellipsoid where e^2 = 2f - f^2
var es = 2 * f - f * f;
this.e_ = Math.sqrt(es);
var m1 = this.calc_m_(phi1, es);
var m2 = this.calc_m_(phi2, es);
var tF = this.calc_t_(phi0, this.e_);
var t1 = this.calc_t_(phi1, this.e_);
var t2 = this.calc_t_(phi2, this.e_);
this.n_ = Math.log(m1 / m2) / Math.log(t1 / t2);
this.F_ = m1 / (this.n_ * Math.pow(t1, this.n_));
this.rho0_ = this.calc_rho_(this.a_, this.F_, tF, this.n_);
}
LambertConformalConic.prototype = new SpatialReference();
/**
* calc_m_
* @param {number} phi
* @param {number} es e square
*/
LambertConformalConic.prototype.calc_m_ = function(phi, es) {
var sinphi = Math.sin(phi);
return Math.cos(phi) / Math.sqrt(1 - es * sinphi * sinphi);
};
/**
* calc_t_
* @param {Object} phi
* @param {Object} e
*/
LambertConformalConic.prototype.calc_t_ = function(phi, e) {
var esp = e * Math.sin(phi);
return Math.tan(Math.PI / 4 - phi / 2) / Math.pow((1 - esp) / (1 + esp), e / 2);
};
/**
* calc_rho (15-7)_
* @param {Object} a
* @param {Object} F
* @param {Object} t
* @param {Object} n
*/
LambertConformalConic.prototype.calc_rho_ = function(a, F, t, n) {
return a * F * Math.pow(t, n);
};
/**
* calc_phi_
* @param {Object} t_i
* @param {Object} e
* @param {Object} phi
*/
LambertConformalConic.prototype.calc_phi_ = function(t, e, phi) {
var esp = e * Math.sin(phi);
return Math.PI / 2 - 2 * Math.atan(t * Math.pow((1 - esp) / (1 + esp), e / 2));
};
/**
* solve phi iteratively.
* @param {Object} t_i
* @param {Object} e
* @param {Object} init
*/
LambertConformalConic.prototype.solve_phi_ = function(t_i, e, init) {
// iteration
var i = 0;
var phi = init;
var newphi = this.calc_phi_(t_i, e, phi);//this.
while (Math.abs(newphi - phi) > 0.000000001 && i < 10) {
i++;
phi = newphi;
newphi = this.calc_phi_(t_i, e, phi);//this.
}
return newphi;
};
/**
* see {@link SpatialReference}
* @param {Array.number} lnglat
* @return {Array.number}
*/
LambertConformalConic.prototype.forward = function(lnglat) {
var phi = lnglat[1] * RAD_DEG;// (Math.PI / 180);
var lamda = lnglat[0] * RAD_DEG;
var t = this.calc_t_(phi, this.e_);
var rho = this.calc_rho_(this.a_, this.F_, t, this.n_);
var theta = this.n_ * (lamda - this.lamda0_);
var E = this.FE_ + rho * Math.sin(theta);
var N = this.FN_ + this.rho0_ - rho * Math.cos(theta);
return [E, N];
};
/**
* see {@link SpatialReference}
* @param {Array.number} coords
* @return {Array.number}
*/
LambertConformalConic.prototype.inverse = function(coords) {
var E = coords[0] - this.FE_;
var N = coords[1] - this.FN_;
var theta = Math.atan(E / (this.rho0_ - N));
var rho = (this.n_ > 0 ? 1 : -1) * Math.sqrt(E * E + (this.rho0_ - N) * (this.rho0_ - N));
var t = Math.pow((rho / (this.a_ * this.F_)), 1 / this.n_);
var init = Math.PI / 2 - 2 * Math.atan(t);
var phi = this.solve_phi_(t, this.e_, init);
var lamda = theta / this.n_ + this.lamda0_;
return [lamda / RAD_DEG, phi / RAD_DEG];
};
/**
* see {@link SpatialReference}
* @return {number}
*/
LambertConformalConic.prototype.getCircum = function() {
return Math.PI * 2 * this.a_;
};
/**
* Create a Transverse Mercator Projection. The <code>params</code> passed in constructor should contain the
* following properties: <br/>
* <code>
* <br/>-wkid: well-known id
* <br/>-semi_major: ellipsoidal semi-major axis in meters
* <br/>-unit: meters per unit
* <br/>-inverse_flattening: inverse of flattening of the ellipsoid where 1/f = a/(a - b)
* <br/>-Scale Factor: scale factor at origin
* <br/>-latitude_of_origin: phi0, latitude of the false origin
* <br/>-central_meridian: lamda0, longitude of the false origin (with respect to the prime meridian)
* <br/>-false_easting: FE, false easting, the Eastings value assigned to the natural origin
* <br/>-false_northing: FN, false northing, the Northings value assigned to the natural origin
* </code>
* <br/>e.g. Georgia West State Plane NAD83 Feet:
* <br/><code> var gawsp83 = new TransverseMercator({wkid: 102667, semi_major:6378137.0,
* inverse_flattening:298.257222101,central_meridian:-84.16666666666667, latitude_of_origin: 30.0,
* scale_factor:0.9999, false_easting:2296583.333333333, false_northing:0, unit: 0.3048006096012192});
* </code>
* @param {Object} params
* @name TransverseMercator
* @constructor
* @class This class (<code>TransverseMercator</code>) represents a Spatial Reference System based on
* <a target = wiki href = 'http://en.wikipedia.org/wiki/Transverse_Mercator_projection'>Transverse Mercator Projection</a>
* @extends SpatialReference
*/
function TransverseMercator(params) {
params = params || {};
SpatialReference.call(this, params);
//GLatLng(33.74561,-84.454308)< === > GPoint(2209149.07977075, 1362617.71496891);
this.a_ = params.semi_major / params.unit;//this.
var f_i = params.inverse_flattening;
this.k0_ = params.scale_factor;
var phi0 = params.latitude_of_origin * RAD_DEG;//(Math.PI / 180);
this.lamda0_ = params.central_meridian * RAD_DEG;
this.FE_ = params.false_easting;//this.
this.FN_ = params.false_northing;//this.
var f = 1.0 / f_i;//this.
/*e: eccentricity of the ellipsoid where e^2 = 2f - f^2 */
this.es_ = 2 * f - f * f;
//var _e = Math.sqrt(this.es_);
/* e^4 */
this.ep4_ = this.es_ * this.es_;
/* e^6 */
this.ep6_ = this.ep4_ * this.es_;
/* e' second eccentricity where e'^2 = e^2 / (1-e^2) */
this.eas_ = this.es_ / (1 - this.es_);
this.M0_ = this.calc_m_(phi0, this.a_, this.es_, this.ep4_, this.ep6_);
}
TransverseMercator.prototype = new SpatialReference();
/**
* calc_m_
* @param {Object} phi
* @param {Object} a
* @param {Object} es
* @param {Object} ep4
* @param {Object} ep6
*/
TransverseMercator.prototype.calc_m_ = function(phi, a, es, ep4, ep6) {
return a * ((1 - es / 4 - 3 * ep4 / 64 - 5 * ep6 / 256) * phi - (3 * es / 8 + 3 * ep4 / 32 + 45 * ep6 / 1024) * Math.sin(2 * phi) + (15 * ep4 / 256 + 45 * ep6 / 1024) * Math.sin(4 * phi) - (35 * ep6 / 3072) * Math.sin(6 * phi));
};
/**
* see {@link SpatialReference}
* @param {Array.number} lnglat
* @return {Array.number}
*/
TransverseMercator.prototype.forward = function(lnglat) {
var phi = lnglat[1] * RAD_DEG;// (Math.PI / 180);
var lamda = lnglat[0] * RAD_DEG;//(Math.PI / 180);
var nu = this.a_ / Math.sqrt(1 - this.es_ * Math.pow(Math.sin(phi), 2));
var T = Math.pow(Math.tan(phi), 2);
var C = this.eas_ * Math.pow(Math.cos(phi), 2);
var A = (lamda - this.lamda0_) * Math.cos(phi);
var M = this.calc_m_(phi, this.a_, this.es_, this.ep4_, this.ep6_);
var E = this.FE_ + this.k0_ * nu * (A + (1 - T + C) * Math.pow(A, 3) / 6 + (5 - 18 * T + T * T + 72 * C - 58 * this.eas_) * Math.pow(A, 5) / 120);
var N = this.FN_ + this.k0_ * (M - this.M0_) + nu * Math.tan(phi) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * Math.pow(A, 4) / 120 + (61 - 58 * T + T * T + 600 * C - 330 * this.eas_) * Math.pow(A, 6) / 720);
return [E, N];
};
/**
* see {@link SpatialReference}
* @param {Array.number} coords
* @return {Array.number}
*/
TransverseMercator.prototype.inverse = function(coords) {
var E = coords[0];
var N = coords[1];
var e1 = (1 - Math.sqrt(1 - this.es_)) / (1 + Math.sqrt(1 - this.es_));
var M1 = this.M0_ + (N - this.FN_) / this.k0_;
var mu1 = M1 / (this.a_ * (1 - this.es_ / 4 - 3 * this.ep4_ / 64 - 5 * this.ep6_ / 256));
var phi1 = mu1 + (3 * e1 / 2 - 27 * Math.pow(e1, 3) / 32) * Math.sin(2 * mu1) + (21 * e1 * e1 / 16 - 55 * Math.pow(e1, 4) / 32) * Math.sin(4 * mu1) + (151 * Math.pow(e1, 3) / 6) * Math.sin(6 * mu1) + (1097 * Math.pow(e1, 4) / 512) * Math.sin(8 * mu1);
var C1 = this.eas_ * Math.pow(Math.cos(phi1), 2);
var T1 = Math.pow(Math.tan(phi1), 2);
var N1 = this.a_ / Math.sqrt(1 - this.es_ * Math.pow(Math.sin(phi1), 2));
var R1 = this.a_ * (1 - this.es_) / Math.pow((1 - this.es_ * Math.pow(Math.sin(phi1), 2)), 3 / 2);
var D = (E - this.FE_) / (N1 * this.k0_);
var phi = phi1 - (N1 * Math.tan(phi1) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * this.eas_) * Math.pow(D, 4) / 24 + (61 + 90 * T1 + 28 * C1 + 45 * T1 * T1 - 252 * this.eas_ - 3 * C1 * C1) * Math.pow(D, 6) / 720);
var lamda = this.lamda0_ + (D - (1 + 2 * T1 + C1) * Math.pow(D, 3) / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * this.eas_ + 24 * T1 * T1) * Math.pow(D, 5) / 120) / Math.cos(phi1);
return [lamda / RAD_DEG, phi / RAD_DEG];
};
/**
* see {@link SpatialReference}
* @return number
*/
TransverseMercator.prototype.getCircum = function() {
return Math.PI * 2 * this.a_;
};
/**
* Creates a Spatial Reference based on Sphere Mercator Projection.
* The <code>params</code> passed in constructor should have the following properties:
* <code><br/>-wkid: wkid
* <br/>-semi_major: ellipsoidal semi-major axis
* <br/>-unit: meters per unit
* <br/>-central_meridian: lamda0, longitude of the false origin (with respect to the prime meridian)
* </code>
* <br/>e.g. The "Web Mercator" used in ArcGIS Server:<br/>
* <code> var web_mercator = new SphereMercator({wkid: 102113, semi_major:6378137.0, central_meridian:0, unit: 1 });
* </code>
* @name SphereMercator
* @class This class (<code>SphereMercator</code>) is the Projection Default Google Maps uses. It is a special form of Mercator.
* @constructor
* @param {Object} params
* @extends SpatialReference
*/
function SphereMercator(params) {
/* =========== parameters = ===================== */
params = params || {};
SpatialReference.call(this, params);
this.a_ = (params.semi_major || 6378137.0) / (params.unit || 1);
this.lamda0_ = (params.central_meridian || 0.0) * RAD_DEG;
}
SphereMercator.prototype = new SpatialReference();
/**
* See {@link SpatialReference}
* @param {Array.number} lnglat
* @return {Array.number}
*/
SphereMercator.prototype.forward = function(lnglat) {
var phi = lnglat[1] * RAD_DEG;
var lamda = lnglat[0] * RAD_DEG;
var E = this.a_ * (lamda - this.lamda0_);
var N = (this.a_ / 2) * Math.log((1 + Math.sin(phi)) / (1 - Math.sin(phi)));
return [E, N];
};
/**
* See {@link SpatialReference}
* @param {Array.number} coords
* @return {Array.number}
*/
SphereMercator.prototype.inverse = function(coords) {
var E = coords[0];
var N = coords[1];
var phi = Math.PI / 2 - 2 * Math.atan(Math.exp(-N / this.a_));
var lamda = E / this.a_ + this.lamda0_;
return [lamda / RAD_DEG, phi / RAD_DEG];
};
/**
* See {@link SpatialReference}
* @return {Number}
*/
SphereMercator.prototype.getCircum = function () {
return Math.PI * 2 * this.a_;
};
/**
* Create a Albers Equal-Area Conic Projection based Spatial Reference. The <code>params</code> passed in construction should
* include the following properties:<code>
* <br/>-wkid: well-known id
* <br/>-semi_major: ellipsoidal semi-major axis in meter
* <br/>-unit: meters per unit
* <br/>-inverse_flattening: inverse of flattening of the ellipsoid where 1/f = a/(a - b)
* <br/>-standard_parallel_1: phi1, latitude of the first standard parallel
* <br/>-standard_parallel_2: phi2, latitude of the second standard parallel
* <br/>-latitude_of_origin: phi0, latitude of the false origin
* <br/>-central_meridian: lamda0, longitude of the false origin (with respect to the prime meridian)
* <br/>-false_easting: FE, false easting, the Eastings value assigned to the natural origin
* <br/>-false_northing: FN, false northing, the Northings value assigned to the natural origin
* </code>
* <br/> e.g.
* <code> var albers = new Albers({wkid:9999, semi_major: 6378206.4,inverse_flattening: 294.9786982,
* standard_parallel_1: 29.5, standard_parallel_2: 45.5,
* central_meridian: -96.0, latitude_of_origin: 23,false_easting: 0,
* 'false_northing': 0, unit: 1 }); </code>
* @name Albers
* @class This class (<code>Albers</code>) represents a Spatial Reference System based on <a target=wiki href = 'http://en.wikipedia.org/wiki/Albers_projection'>Albers Projection</a>.
* @extends SpatialReference
* @constructor
* @param {Object} params
*/
function Albers(params) {
//http://pubs.er.usgs.gov/djvu/PP/PP_1395.pdf, page 101 & 292
//for NAD_1983_Alaska_Albers: LatLng()< === > Point();
params = params || {};
SpatialReference.call(this, params);
var f_i = params.inverse_flattening;
var phi1 = params.standard_parallel_1 * RAD_DEG;
var phi2 = params.standard_parallel_2 * RAD_DEG;
var phi0 = params.latitude_of_origin * RAD_DEG;
this.a_ = params.semi_major / params.unit;
this.lamda0_ = params.central_meridian * RAD_DEG;
this.FE_ = params.false_easting;
this.FN_ = params.false_northing;
var f = 1.0 / f_i; //e: eccentricity of the ellipsoid where e^2 = 2f - f^2
var es = 2 * f - f * f;
this.e_ = Math.sqrt(es);
var m1 = this.calc_m_(phi1, es);
var m2 = this.calc_m_(phi2, es);
var q1 = this.calc_q_(phi1, this.e_);
var q2 = this.calc_q_(phi2, this.e_);
var q0 = this.calc_q_(phi0, this.e_);
this.n_ = (m1 * m1 - m2 * m2) / (q2 - q1);
this.C_ = m1 * m1 + this.n_ * q1;
this.rho0_ = this.calc_rho_(this.a_, this.C_, this.n_, q0);
};
Albers.prototype = new SpatialReference();
/**
* calc_m_
* @param {number} phi
* @param {number} es e square
*/
Albers.prototype.calc_m_ = function(phi, es) {
var sinphi = Math.sin(phi);
return Math.cos(phi) / Math.sqrt(1 - es * sinphi * sinphi);
};
/**
* formular (3-12) page 101
* @param {Object} phi
* @param {Object} e
*/
Albers.prototype.calc_q_ = function(phi, e) {
var esp = e * Math.sin(phi);
return (1 - e * e) * (Math.sin(phi) / (1 - esp * esp) - (1 / (2 * e)) * Math.log((1 - esp) / (1 + esp)));
};
Albers.prototype.calc_rho_ = function(a, C, n, q) {
return a * Math.sqrt(C - n * q) / n;
};
Albers.prototype.calc_phi_ = function(q, e, phi) {
var esp = e * Math.sin(phi);
return phi + (1 - esp * esp) * (1 - esp * esp) / (2 * Math.cos(phi)) * (q / (1 - e * e) - Math.sin(phi) / (1 - esp * esp) + Math.log((1 - esp) / (1 + esp)) / (2 * e));
};
Albers.prototype.solve_phi_ = function(q, e, init) {
// iteration
var i = 0;
var phi = init;
var newphi = this.calc_phi_(q, e, phi);
while (Math.abs(newphi - phi) > 0.00000001 && i < 10) {
i++;
phi = newphi;
newphi = this.calc_phi_(q, e, phi);
}
return newphi;
};
/**
* see {@link SpatialReference}
* @param {Array.number} lnglat
* @return {Array.number}
*/
Albers.prototype.forward = function(lnglat) {
var phi = lnglat[1] * RAD_DEG;
var lamda = lnglat[0] * RAD_DEG;
var q = this.calc_q_(phi, this.e_);
var rho = this.calc_rho_(this.a_, this.C_, this.n_, q);
var theta = this.n_ * (lamda - this.lamda0_);
var E = this.FE_ + rho * Math.sin(theta);
var N = this.FN_ + this.rho0_ - rho * Math.cos(theta);
return [E, N];
};
/**
* see {@link SpatialReference}
* @param {Array.number} coords
* @return {Array.number}
*/
Albers.prototype.inverse = function(coords) {
var E = coords[0] - this.FE_;
var N = coords[1] - this.FN_;
var rho = Math.sqrt(E * E + (this.rho0_ - N) * (this.rho0_ - N));
var adj = this.n_ > 0 ? 1 : -1;
var theta = Math.atan(adj * E / (adj * this.rho0_ - adj * N));
var q = (this.C_ - rho * rho * this.n_ * this.n_ / (this.a_ * this.a_)) / this.n_;
var init = Math.asin(q / 2);
var phi = this.solve_phi_(q, this.e_, init);
var lamda = theta / this.n_ + this.lamda0_;
return [lamda / RAD_DEG, phi / RAD_DEG];
};
/**
* see {@link SpatialReference}
* @return number
*/
Albers.prototype.getCircum = function() {
return Math.PI * 2 * this.a_;
};
/**
* See {@link SpatialReference}
* @return {number}
*/
Albers.prototype.getCircum = function() {
return Math.PI * 2 * this.a_;
};
WGS84 = new Geographic({
wkid: 4326
});
NAD83 = new Geographic({
wkid: 4269
});
WEB_MERCATOR = new SphereMercator({
wkid: 102113,
semi_major: 6378137.0,
central_meridian: 0,
unit: 1
});
WEB_MERCATOR_AUX = new SphereMercator({
wkid: 102100,
semi_major: 6378137.0,
central_meridian: 0,
unit: 1
});
// declared early but assign here to avoid dependency error by jslint
spatialReferences_ = {
'4326': WGS84,
'4269': NAD83,
'102113': WEB_MERCATOR,
'102100': WEB_MERCATOR_AUX
};
SpatialReference.WGS84 = WGS84;
SpatialReference.NAD83 = NAD83;
//TODO: check advanced compile impact
SpatialReference.WEB_MERCATOR = WEB_MERCATOR;
SpatialReference.WEB_MERCATOR_AUX = WEB_MERCATOR_AUX;
/**
* <b> static</b> method. Call with Syntax <code>SpatialReference.register(..)</code>.
* Add A Spatial Reference to the internal collection of Spatial References.
* the <code>wktOrSR</code> parameter can be String format of "well-known text" of the
* Spatial Reference, or an instance of {@link SpatialReference}.
* <br/><li> If passes in String WKT format, to be consistent, it should use the same format as listed
* in <a href = 'http://edndoc.esri.com/arcims/9.2/elements/pcs.htm'>
* ESRI documentation</a>. For example, add NC State Plane NAD83 as String:
* <br/><code>
* SpatialReference.register(2264,'PROJCS["NAD_1983_StatePlane_North_Carolina_FIPS_3200_Feet",
* GEOGCS["GCS_North_American_1983",
* DATUM["D_North_American_1983",
* SPHEROID["GRS_1980",6378137.0,298.257222101]],
* PRIMEM["Greenwich",0.0],
* UNIT["Degree",0.0174532925199433]],
* PROJECTION["Lambert_Conformal_Conic"],
* PARAMETER["False_Easting",2000000.002616666],
* PARAMETER["False_Northing",0.0],
* PARAMETER["Central_Meridian",-79.0],
* PARAMETER["Standard_Parallel_1",34.33333333333334],
* PARAMETER["Standard_Parallel_2",36.16666666666666],
* PARAMETER["Latitude_Of_Origin",33.75],
* UNIT["Foot_US",0.3048006096012192]]');
* <br/></code>
* Note: only <b>Lambert Conformal Conic</b> and <b>Transverse Mercator</b> Projection
* based Spatial References are supported if added via WKT String.
* <br/><li> If passes in an instance of {@link SpatialReference}, it can be one of the
* built in classes, or a class that extends SpatialReference. For example, add NC State Plane NAD83 as SR:
* <br/><code>
* SpatialReferences.register(2264: new LambertConformalConic({
* wkid: 2264,
* semi_major: 6378137.0,
* inverse_flattening: 298.257222101,
* standard_parallel_1: 34.33333333333334,
* standard_parallel_2: 36.16666666666666,
* central_meridian: -79.0,
* latitude_of_origin: 33.75,
* 'false_easting': 2000000.002616666,
* 'false_northing': 0,
* unit: 0.3048006096012192
* });
* <br/></code>
* @static
* @param {Number|String} wkid/wkt
* @param {Object} wktOrSR
* @return {SpatialReference} registered SR
*/
Util.registerSR = function(wkidt, wktOrSR) {
var sr = spatialReferences_['' + wkidt];
if (sr) {
return sr;
}
if (wktOrSR instanceof SpatialReference) {
spatialReferences_['' + wkidt] = wktOrSR;
sr = wktOrSR;
} else {
var wkt = wktOrSR || wkidt; // only one param is passed in.
var params = {
'wkt': wkidt
};
if (wkidt === parseInt(wkidt, 10)) {
params = {
'wkid': wkidt
};
}
var prj = extractString_(wkt, "PROJECTION[\"", "\"]");
var spheroid = extractString_(wkt, "SPHEROID[", "]").split(",");
if (prj !== "") {
params.unit = parseFloat(extractString_(extractString_(wkt, "PROJECTION", ""), "UNIT[", "]").split(",")[1]);
params.semi_major = parseFloat(spheroid[1]);
params.inverse_flattening = parseFloat(spheroid[2]);
params.latitude_of_origin = parseFloat(extractString_(wkt, "\"Latitude_Of_Origin\",", "]"));
params.central_meridian = parseFloat(extractString_(wkt, "\"Central_Meridian\",", "]"));
params.false_easting = parseFloat(extractString_(wkt, "\"False_Easting\",", "]"));
params.false_northing = parseFloat(extractString_(wkt, "\"False_Northing\",", "]"));
}
switch (prj) {
case "":
sr = new SpatialReference(params);
break;
case "Lambert_Conformal_Conic":
params.standard_parallel_1 = parseFloat(extractString_(wkt, "\"Standard_Parallel_1\",", "]"));
params.standard_parallel_2 = parseFloat(extractString_(wkt, "\"Standard_Parallel_2\",", "]"));
sr = new LambertConformalConic(params);
break;
case "Transverse_Mercator":
params.scale_factor = parseFloat(extractString_(wkt, "\"Scale_Factor\",", "]"));
sr = new TransverseMercator(params);
break;
case "Albers":
params.standard_parallel_1 = parseFloat(extractString_(wkt, "\"Standard_Parallel_1\",", "]"));
params.standard_parallel_2 = parseFloat(extractString_(wkt, "\"Standard_Parallel_2\",", "]"));
sr = new Albers(params);
break;
// more implementations here.
default:
throw new Error(prj + " not supported");
}
if (sr) {
spatialReferences_['' + wkidt] = sr;
}
}
return sr;
};
//end of projection related code//
/**
* @name Error
* @class Error returned from Server.
* Syntax:
* <pre>
* {
"error" :
{
"code" : 500,
"message" : "Object reference not set to an instance of an object.",
"details" : [
"'geometry' parameter is invalid"
]
}
}
</pre>
*/
/**
* Create a ArcGIS service catalog instance using it's url:<code> http://<host>/<instance>/rest/services</code>
* @name Catalog
* @constructor
* @class The catalog resource is the root node and initial entry point into an ArcGIS Server host.
* This resource represents a catalog of folders and services published on the host.
* @param {String} url
* @property {String} [currentVersion] currentVersion
* @property {Array.string} [folders] folders list
* @property {Array.string} [services] list of services. Each has <code>name, type</code> property.
*/
function Catalog(url) {
this.url = url;
var me = this;
getJSON_(url, {}, '', function(json) {
augmentObject_(json, me);
/**
* This event is fired when the catalog info is loaded.
* @name Catalog#load
* @event
*/
triggerEvent_(me, 'load');
});
}
/**
* @name Field
* @class This class represents a field in a {@link Layer}. It is accessed from
* the <code> fields</code> property. There is no constructor for this class,
* use Object Literal.
* @property {String} [name] field Name
* @property {String} [type] field type (esriFieldTypeOID|esriFieldTypeString|esriFieldTypeInteger|esriFieldTypeGeometry}.
* @property {String} [alias] field alias.
* @property {Domain} [domain] domain
* @property {Int} [length] length.
*/
/**
* Create a ArcGIS map Layer using it's url (http://[mapservice-url]/[layerId])
* @name Layer
* @class This class (<code>Layer</code>) The layer / table(v10+)
* resource represents a single layer / table in a map of a map service
* published by ArcGIS Server.
* @constructor
* @param {String} url
* @property {Number} [id] layer ID
* @property {String} [name] layer Name
* @property {String} [type] Feature Layer|Image Layer
* @property {String} [description] description
* @property {String} [definitionExpression] Layer definition.
* @property {String} [geometryType] geometryType type(esriGeometryPoint|..), only available after load.
* @property {String} [copyrightText] copyrightText, only available after load.
* @property {Layer} [parentLayer] parent Layer {@link Layer}
* @property {Boolean} [defaultVisibility] defaultVisibility
* @property {Array.Layer} [subLayers] sub Layers. {@link Layer}.
* @property {Boolean} [visibility] Visibility of this layer
* @property {Number} [minScale] minScale
* @property {Number} [maxScale] maxScale
* @property {TimeInfo} [timeInfo] timeInfo
* @property {DrawingInfo} [drawingInfo] rendering info See {@link DrawingInfo}
* @property {Boolean} [hasAttachments] hasAttachments
* @property {String} [typeIdField] typeIdField
* @property {Array.Field} [fields] fields, only available after load. See {@link Field}
* @property {Array.String} [types] subtypes: id, name, domains.
* @property {Array.String} [relationships] relationships (id, name, relatedTableId)
*/
function Layer(url) {
this.url = url;
this.definition = null;
}
/**
* Load extra information such as it's fields from layer resource.
* If opt_callback function will be called after it is loaded
*/
Layer.prototype.load = function() {
var me = this;
if (this.loaded_) {
return;
}
getJSON_(this.url, {}, '', function (json) {
augmentObject_(json, me);
me.loaded_ = true;
/**
* This event is fired when layer's service info is loaded.
* @name Layer#load
* @event
*/
triggerEvent_(me, 'load');
});
};
/**
* Whether the layer is viewable at given scale
* @param {Number} scale
* @return {Boolean}
*/
Layer.prototype.isInScale = function(scale) {
// note if the layer's extra info is not loaded, it will return true
if (this.maxScale && this.maxScale > scale) {
return false;
}
if (this.minScale && this.minScale < scale) {
return false;
}
return true;
};
/**
* @name SpatialRelationship
* @enum
* @class This is actually a list of constants that represent spatial
* relationship types.
* @property {String} [INTERSECTS] esriSpatialRelIntersects
* @property {String} [CONTAINS] esriSpatialRelContains
* @property {String} [CROSSES] esriSpatialRelCrosses
* @property {String} [ENVELOPE_INTERSECTS] esriSpatialRelEnvelopeIntersects
* @property {String} [INDEX_INTERSECTS] esriSpatialRelIndexIntersects
* @property {String} [OVERLAPS] esriSpatialRelOverlaps
* @property {String} [TOUCHES] esriSpatialRelTouches
* @property {String} [WITHIN] esriSpatialRelWithin
*/
var SpatialRelationship = {
INTERSECTS: 'esriSpatialRelIntersects',
CONTAINS: 'esriSpatialRelContains',
CROSSES: 'esriSpatialRelCrosses',
ENVELOPE_INTERSECTS: 'esriSpatialRelEnvelopeIntersects',
INDEX_INTERSECTS: 'esriSpatialRelIndexIntersects',
OVERLAPS: 'esriSpatialRelOverlaps',
TOUCHES: 'esriSpatialRelTouches',
WITHIN: 'esriSpatialRelWithin'
};
/**
* @name QueryOptions
* @class This class represent the parameters needed in an query operation for a {@link Layer}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/query.html'>Query Operation</a>.
* @property {String} [text] A literal search text. If the layer has a display field
* associated with it, the server searches for this text in this field.
* This parameter is a short hand for a where clause of:
* where [displayField]like '%[text]%'. The text is case sensitive.
* This parameter is ignored if the where parameter is specified.
* @property {OverlayView|Array.OverlayView} [geometry] The geometry to apply as the spatial filter.
* @property {SpatialRelationship} [spatialRelationship] The spatial relationship to be applied on the
* input geometry while performing the query. The supported spatial relationships
* include intersects, contains, envelope intersects, within, etc.
* The default spatial relationship is intersects. See {@link SpatialRelationship}
* @property {String} [where] A where clause for the query filter. Any legal SQL where clause operating on the fields in the layer is allowed.
* @property {Array.string} [outFields] The list of fields to be included in the returned resultset.
* @property {Boolean} [returnGeometry] If true, If true, the resultset will include the geometries associated with each result.
* @property {Array.number} [objectIds] The object IDs of this layer / table to be queried
* @property {Number} [maxAllowableOffset] This option can be used to specify the maximum allowable offset to be used for generalizing geometries returned by the query operation
* @property {Boolean} [returnIdsOnly] If true, the response only includes an array of object IDs. Otherwise the response is a feature set. The default is false.
* @property {OverlayOptions} [overlayOptions] See {@link OverlayOptions}
*/
/**
* @name ResultSet
* @class This class represent the results of an query operation for a {@link Layer}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/query.html'>Query Operation</a>.
* @property {String} [displayFieldName] display Field Name for layer
* @property {Object} [fieldAliases] Field Name's Aliases. key is field name, value is alias.
* @property {GemetryType} [geometryType] esriGeometryPoint | esriGeometryMultipoint | esriGeometryPolygon | esriGeometryPolyline
* @property {Array.feature} [features] result as array of {@link Feature}
* @property {String} [objectIdFieldName] objectIdFieldName when returnIdsOnly=true
* @property {Array.int} [objectIds] objectIds when returnIdsOnly=true
*/
/**
* The query operation is performed on a layer resource. The result of this operation is a resultset resource that will be
* passed in the callback function. param is an instance of {@link QueryOptions}
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/query.html'>Query Operation</a>.
* @param {QueryOptions} params
* @param {Function} callback
* @param {Function} errback
*/
Layer.prototype.query = function(p, callback, errback) {
if (!p) {
return;
}
// handle text, where, relationParam, objectIds, maxAllowableOffset
var params = augmentObject_(p, {});
if (p.geometry && !isString_(p.geometry)) {
params.geometry = fromOverlaysToJSON_(p.geometry);
params.geometryType = getGeometryType_(p.geometry);
params.inSR = 4326;
}
if (p.spatialRelationship) {
params.spatialRel = p.spatialRelationship;
delete params.spatialRelationship;
}
if (p.outFields && isArray_(p.outFields)) {
params.outFields = p.outFields.join(',');
}
if (p.objectIds) {
params.objectIds = p.objectIds.join(',');
}
if (p.time) {
params.time = formatTimeString_(p.time, p.endTime);
}
params.outSR = 4326;
params.returnGeometry = p.returnGeometry === false ? false : true;
params.returnIdsOnly = p.returnIdsOnly === true ? true : false;
delete params.overlayOptions;
getJSON_(this.url + '/query', params, '', function(json) {
parseFeatures_(json.features, p.overlayOptions);
callback(json, json.error);
handleErr_(errback, json);
});
};
/**
* @name QueryRelatedRecordsOptions
* @class This class represent the parameters needed in an query related records operation for a {@link Layer}.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/queryrelatedrecords.html'>Query Related Records Operation</a>.
* @property {Array.number} [objectIds] The object IDs of this layer / table to be queried
* @property {Int} [relatioshipId] The ID of the relationship to be queried
* @property {Array.string} [outFields] The list of fields to be included in the returned resultset. This list is a comma delimited list of field names.
* @property {String} [definitionExpression] The definition expression to be applied to the related table / layer. From the list of objectIds, only those records that conform to this expression will be returned.
* @property {Boolean} [returnGeometry = true] If true, the resultset will include the geometries associated with each result.
* @property [Number] [maxAllowableOffset] This option can be used to specify the maximum allowable offset to be used for generalizing geometries returned by the query operation
* @property {Number} [outSR] The well-known ID of or the {@link SpatialReference} of the output geometries
*/
/**
* @name RelatedRecords
* @class This class represent the results of an query related records operation for a {@link Layer}.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/queryrelatedrecords.html'>Query Operation</a>.
* @property {String} [geometryType] esriGeometryPoint | esriGeometryMultipoint | esriGeometryPolygon | esriGeometryPolyline
* @property {Object} [spatialReference] {@link SpatialReference}
* @property {String} [displayFieldName] display Field Name for layer
* @property {Array.object} [relatedRecordGroups] list of related records
*/
/**
* @name RelatedRecord
* @class This class represent the result of an query related records operation for a {@link Layer}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/queryrelatedrecords.html'>Query Operation</a>.
* @property {int} [objectId] objectid of original record
* @property {Array.feature} [relatedRecords] list of {@link Feature}s.
*/
/**
* The query related records operation is performed on a layer / table resource.
* The result of this operation are featuresets grouped by source layer / table
* object IDs. Each featureset contains Feature objects including the values for
* the fields requested by the user. For related layers, if you request geometry
* information, the geometry of each feature is also returned in the featureset.
* For related tables, the featureset does not include geometries.
* @param {QueryRelatedRecordsParameters} params
* @param {Function} callback
* @param {Function} errback
*/
Layer.prototype.queryRelatedRecords = function(qparams, callback, errback) {
if (!qparams) {
return;
}
var params = augmentObject_(qparams, {});
params.f = params.f || 'json';
if (params.outFields && !isString_(params.outFields)) {
params.outFields = params.outFields.join(',');
}
params.returnGeometry = params.returnGeometry === false ? false : true;
getJSON_(this.url + '/query', params, '', function (json) {
handleErr_(errback, json);
callback(json);
});
};
/**
* @name MapSerivceOptions
* @class provides options to construct a {@link MapService}
* @property {Number} delayLoad number of seconds to delay loading meta data on construction.
*/
/**
* Creates a MapService objects that can be used by UI components.
* <ul><li> <code> url</code> (required) is the URL of the map servive, e.g. <code>
* http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer</code>.
* <ul/> Note the spatial reference of the map service must already exists
* in the {@link spatialReferences_} if actual coordinates transformation is needed.
* @name MapService
* @class This class (<code>MapService</code>) is the core class for all map service operations.
* It represents an ArcGIS Server map service that offer access to map and layer content
* @constructor
* @param {String} url
* @property {String} [url] map service URL
* @property {String} [serviceDescription] serviceDescription
* @property {String} [mapName] map frame Name inside the map document
* @property {String} [description] description
* @property {String} [copyrightText] copyrightText
* @property {Array.Layer} [layers] array of {@link Layer}s.
* @property {Array.Layer} [tables] array of Tables of type {@link Layer}.
* @property {SpatialReference} [spatialReference] see {@link SpatialReference}
* @property {Boolean} [singleFusedMapCache] if map cache is singleFused
* @property {TileInfo} [tileInfo] See {@link TileInfo}
* @property {TimeInfo} [timeInfo] see {@link TimeInfo}
* @property {String} [units] unit
* @property {String} [supportedImageFormatTypes] supportedImageFormatTypes, comma delimited list.
* @property {Object} [documentInfo] Object with the folloing properties: <code>Title, Author,Comments,Subject,Category,Keywords</code>
*/
function MapService(url, opts) {
this.url = url;
this.loaded_ = false;
var tks = url.split("/");
this.name = tks[tks.length - 2].replace(/_/g, ' ');
opts = opts || {};
if (opts.delayLoad) {
var me = this;
window.setTimeout(function() {
me.loadServiceInfo();
}, opts.delayLoad * 1000);
} else {
this.loadServiceInfo();
}
}
/**
* Load serviceInfo
*/
MapService.prototype.loadServiceInfo = function() {
var me = this;
getJSON_(this.url, {}, '', function(json) {
me.init_(json);
});
};
/**
* initialize an ArcGIS Map Service from the meta data information.
* The <code>json</code> parameter is the json object returned by Map Service.
* @private
* @param {Object} json
*/
MapService.prototype.init_ = function (json) {
var me = this;
if (json.error) {
throw new Error(json.error.message);
}
augmentObject_(json, this);
if (json.spatialReference.wkt) {
this.spatialReference = Util.registerSR(json.spatialReference.wkt);
} else {
this.spatialReference = spatialReferences_[json.spatialReference.wkid];
}
if (json.tables !== undefined) {
// v10.0 +
getJSON_(this.url + '/layers', {}, '', function (json2) {
me.initLayers_(json2);
// V10 SP1
getJSON_(me.url + '/legend', {}, '', function (json3){
me.initLegend_(json3);
me.setLoaded_();
});
});
} else {
// v9.3
me.initLayers_(json);
me.setLoaded_();
}
};
MapService.prototype.setLoaded_ = function() {
this.loaded_ = true;
/**
* This event is fired when the service and it's service info is loaded.
* @name MapService#load
* @event
*/
triggerEvent_(this, "load");
};
/**
* initialize an Layers.
* The <code>json</code> parameter is the json object returned by Map Service or layers operation(v10+).
* @private
* @param {Object} json2
*/
MapService.prototype.initLayers_ = function (json2) {
var layers = [];
var tables = [];
this.layers = layers;
if (json2.tables) {
this.tables = tables;
}
var layer, i, c, info;
for (i = 0, c = json2.layers.length; i < c; i++) {
info = json2.layers[i];
layer = new Layer(this.url + '/' + info.id);
augmentObject_(info, layer);
layer.visible = layer.defaultVisibility;
layers.push(layer);
}
if (json2.tables) {
for (i = 0, c = json2.tables.length; i < c; i++) {
info = json2.tables[i];
layer = new Layer(this.url + '/' + info.id);
augmentObject_(info, layer);
tables.push(layer);
}
}
for (i = 0, c = layers.length; i < c; i++) {
layer = layers[i];
if (layer.subLayerIds) {
layer.subLayers = [];
for (var j = 0, jc = layer.subLayerIds.length; j < jc; j++) {
var subLayer = this.getLayer(layer.subLayerIds[j]);
layer.subLayers.push(subLayer);
subLayer.parentLayer = layer;
}
}
}
};
/**
* initialize an Layers.
* The <code>json</code> parameter is the json object returned by Map Service or layers operation(v10+).
* @private
* @param {Object} json2
*/
MapService.prototype.initLegend_ = function(json3) {
// if not AGS10 SP1, server will return error.
var layers = this.layers;
if (json3.layers) {
var layer, i, c, info;
for (i = 0, c = json3.layers.length; i < c; i++) {
info = json3.layers[i];
layer = layers[info.layerId]; // layers id should same as index.
augmentObject_(info, layer);
}
}
};
/**
* Get a map layer by it's name(String) or id (Number), return {@link Layer}.
* @param {String|Number} nameOrId
* @return {Layer}
*/
MapService.prototype.getLayer = function (nameOrId) {
var layers = this.layers;
if (layers) {
for (var i = 0, c = layers.length; i < c; i++) {
if (nameOrId === layers[i].id) {
return layers[i];
}
if (isString_(nameOrId) && layers[i].name.toLowerCase() === nameOrId.toLowerCase()) {
return layers[i];
}
}
}
return null;
};
/**
* Get the layer definitions.
* @return {Object} key as id, value as string of definition expression.
*/
MapService.prototype.getLayerDefs_ = function() {
var ret = {};
if (this.layers) {
for (var i = 0, c = this.layers.length; i < c; i++) {
var layer = this.layers[i];
if (layer.definition) {
ret[String(layer.id)] = layer.definition;
}
}
}
return ret;
};
/**
* If the map service meta info is loaded
* @return {Boolean}
*/
MapService.prototype.hasLoaded = function () {
return this.loaded_;
}
/**
* get a list of visible layer's Ids
* @return {Array.number} null if not initialized
*/
MapService.prototype.getVisibleLayerIds_ = function () {
var ret = [];
if (this.layers) { // in case service not loaded_
var layer;
// a special behavior of REST (as of 9.3.1):
// if partial group then parent must be off
var i, c;
for (i = 0, c = this.layers.length; i < c; i++) {
layer = this.layers[i];
if (layer.subLayers) {
for (var j = 0, jc = layer.subLayers.length; j < jc; j++) {
if (layer.subLayers[j].visible === false) {
layer.visible = false;
break;
}
}
}
}
for (i = 0, c = this.layers.length; i < c; i++) {
layer = this.layers[i];
//2010-10-26: in AGS10, group layer behavior is opposite of 9.3.1. And UNDOUMENTED in REST API!
if (layer.subLayers && layer.subLayers.length > 0) {
continue;
}
if (layer.visible === true) {
ret.push(layer.id);
}
}
}
return ret;
};
/**
* get initial bounds of the map serivce
* @return {google.maps.LatLngBounds}
*/
MapService.prototype.getInitialBounds = function () {
if (this.initialExtent) {
this.initBounds_ = this.initBounds_ || fromEnvelopeToLatLngBounds_(this.initialExtent);
return this.initBounds_;
}
return null;
};
/**
* get full bounds of the map serivce
* @return {google.maps.LatLngBounds}
*/
MapService.prototype.getFullBounds = function () {
if (this.fullExtent) {
this.fullBounds_ = this.fullBounds_ || fromEnvelopeToLatLngBounds_(this.fullExtent)
return this.fullBounds_;
}
return null;
};
/**
* @name ExportMapOptions
* @class This class represent the parameters needed in an exportMap operation for a {@link MapService}.
* <br/>For more info see <a href='http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/export.html'>Export Operation</a>.
* @property {Number} [width] width of image, in pixel;
* @property {Number} [height] height of image, in pixel;
* @property {SpatialReference} [imageSR] The well-known ID of the spatial reference of the exported image or instance of {@link SpatialReference}.
* @property {String} [format = png] The format of the exported image. png | png8 | png24 | jpg | pdf | bmp | gif | svg
* @property {Number} [dpi] The dpi of the exported image, default 96
* @property {Object} [layerDefinitions] Allows you to filter the features of individual layers in the exported map by specifying
* definition expressions for those layers. Syntax: { "<layerId1>" : "<layerDef1>" , "<layerId2>" : "<layerDef2>" }
* key is layerId returned by server, value is definition for that layer.
* @property {Array.number} [layerIds] list of layer ids. If not specified along with layerOptions, show list of visible layers.
* @property {String} [layerOptions] show | hide | include | exclude. If not specified with along layerIds, show list of visible layers.
* @property {Boolean} [transparent = true] If true, the image will be exported with
* the background color of the map set as its transparent color. note the REST API default value is false.
* @property {google.maps.LatLngBounds} [bounds] bounds of map
* @property {Date} [time] The time instant the exported map image if the service supports time (since AGS10).
* @property {Date} [endTime] The end time instant the exported map image if the service supports time (since AGS10).
* time=<timeInstant> or time=<startTime>, <endTime>, e.g. time=1199145600000, 1230768000000 (1 Jan 2008 00:00:00 GMT to 1 Jan 2009 00:00:00 GMT)
* @property {Object} [layerTimeOptions] layerTimeOptions The time options per layer. Users can indicate whether or not the layer should use the time extent
* specified by the time parameter or not, whether to draw the layer
* features cumulatively or not and the time offsets for the layer. Syntax: <pre>
* {
"<layerId1>" : {
//If true, use the time extent specified by the time parameter
"useTime" : < true | false >,
//If true, draw all the features from the beginning of time for that data
"timeDataCumulative" : < true | false >,
//Time offset for this layer so that it can be overlaid on the top of a previous or future time period
"timeOffset" : <timeOffset1>,
"timeOffsetUnits" : "<esriTimeUnitsCenturies | esriTimeUnitsDays | esriTimeUnitsDecades |
esriTimeUnitsHours | esriTimeUnitsMilliseconds | esriTimeUnitsMinutes |
esriTimeUnitsMonths | esriTimeUnitsSeconds | esriTimeUnitsWeeks | esriTimeUnitsYears |
esriTimeUnitsUnknown>"
},
"<layerId2>" : {
"useTime" : < true | false >,
"timeDataCumulative" : < true | false >,
"timeOffsetOffset" : <timeOffset2>,
"timeOffsetUnits" : "<timeOffsetUnits2>"
}
}
</pre>
*/
/**
* @name MapImage
* @class This is the result of {@link MapService}.exportMap operation.
* There is no constructor, use as JavaScript object literal.
* @property {String} [href] URL of image
* @property {google.maps.LatLngBounds} [bounds] The bounding box of the exported image.
* @property {Number} [width] width of the exported image.
* @property {Number} [height] height of the exported image.
* @property {Number} [scale] scale of the exported image.
*/
/**
* Export an image with given parameters.
* For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/export.html'>Export Operation</a>.
* <br/> The <code>params</code> is an instance of {@link ExportMapOptions}.
* The following properties will be set automatically if not specified:...
* <br/> The <code>callback</code> is the callback function with argument of
* an instance of {@link MapImage}.
* @param {ExportMapOptions} params
* @param {Function} callback
* @param {Function} errback
* @return {String|None} url of image if f=image, none if f=json
*/
MapService.prototype.exportMap = function (p, callback, errback) {
if (!p || !p.bounds) {
return;
}
// note: dynamic map may overlay on top of maptypes with different projection
var params = {};// augmentObject_(p, );
params.f = p.f;
var bnds = p.bounds;
var swx = bnds.getSouthWest().lng();
var nex = bnds.getNorthEast().lng();
if (swx> nex){
swx = swx-180;
// nex = nex +180;
}
params.bbox = '' + swx + ',' + '' + bnds.getSouthWest().lat() + ',' +
nex +
',' +
'' +
bnds.getNorthEast().lat();
//delete params.bounds;
//log_('send '+bnds.toUrlValue());
params.size = '' + p.width + ',' + p.height;
params.dpi = p.dpi;
if (p.imageSR) {
if (p.imageSR.wkid) {
params.imageSR = p.imageSR.wkid;
} else {
params.imageSR = '{wkt:' + p.imageSR.wkt + '}';
}
}
params.bboxSR = '4326';
params.format = p.format;
var defs = p.layerDefinitions;
// there is a slightly difference between {} and undefined
// if do not want use def at all, pass in {}, if want to use
// in service, do not pass in anything.
if (defs === undefined) {
defs = this.getLayerDefs_();
}
// for 9.3 compatibility:
params.layerDefs = getLayerDefsString_(defs);
var vlayers = p.layerIds;
var layerOpt = p.layerOption || 'show';
if (vlayers === undefined) {
vlayers = this.getVisibleLayerIds_();
}
if (vlayers.length > 0) {
params.layers = layerOpt + ':' + vlayers.join(',');
} else {
// no layers visible, no need to go to server, note if vlayers is null means not init yet in which case do not send layers
if (this.loaded_ && callback) {
callback({
href: null
});
return;
}
}
params.transparent = (p.transparent === false ? false : true);
if (p.time) {
params.time = formatTimeString_(p.time, p.endTime);
}
//TODO: finish once v10 released
params.layerTimeOptions = p.layerTimeOptions;
if (params.f === 'image') {
return this.url + '/export?' + formatParams_(params);
} else {
getJSON_(this.url + '/export', params, '', function (json) {
if (json.extent) {
json.bounds = fromEnvelopeToLatLngBounds_(json.extent);
//log_('got '+json.bounds.toUrlValue());
delete json.extent;
callback(json);
} else {
handleErr_(errback, json.error);
}
});
}
};
/**
* @name Feature
* @class This class represent JSON feature object as returned by the REST API.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/feature.html'>Feature Object</a>.
* Syntax:
* <pre>
{
"geometry" : <overlays>,
"attributes" : {
"name1" : <value1>,
"name2" : <value2>,
}
}
* </pre>
* @property {Array.OverlayView} [geometry] geometries. Array of Marker, Polyline or Polygon.
* @property {Object} [attributes] attributes as name-value JSON object.
*/
/**
* @name IdentifyOptions
* @class This class represent the parameters needed in an identify operation for a {@link MapService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/identify.html'>Identify Operation</a>.
* @property {Geometry} [geometry] The geometry to identify on, <code>google.maps.LatLng</code>, <code>Polyline</code>, or <code>Polygon</code>.
* @property {Array.number} [layerIds] The layers to perform the identify operation on.
* @property {String} [layerOption] The layers to perform the identify operation on. 'top|visible|all'.
* @property {Number} [tolerance] The distance in screen pixels from the specified geometry within which the identify should be performed
* @property {google.maps.LatLngBounds} [bounds] The bounding box of the map currently being viewed.
* @property {Number} [width] width of image in pixel
* @property {Number} [height] height of image in pixel
* @property {Number} [dpi] dpi of image, default 96;
* @property {Boolean} [returnGeometry = true] If true, the resultset will include the geometries associated with each result.
* @property {Number} [maxAllowableOffset] This option can be used to specify the maximum allowable offset to be used for generalizing geometries returned by the identify operation
* @property {OverlayOptions} [overlayOptions] how results should be rendered. See {@link OverlayOptions}
*/
/**
* @name IdentifyResults
* @class This class represent the results of an identify operation for
* a {@link MapService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/identify.html'>Identify Operation</a>.
* @property {Array.IdentifyResult} [results] The identify results as an array of {@link IdentifyResult}
*/
/**
* @name IdentifyResult
* @class This class represent one entry in the results of an identify operation for a {@link MapService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/identify.html'>Identify Operation</a>.
* @property {Number} [layerId] layerId
* @property {String} [layerName] layerName
* @property {String} [value] value of the display field
* @property {String} [displayFieldName] displayFieldName
* @property {Feature} [feature] {@link Feature}
*/
/**
* Identify features on a particular Geographic location, using {@link IdentifyOptions} and
* process {@link IdentifyResults} using the <code>callback</code> function.
* For more info see <a
* href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/identify.html'>Identify Operation</a>.
* @param {IdentifyOptions} params
* @param {Function} callback
* @param {Function} errback
*/
MapService.prototype.identify = function (p, callback, errback) {
if (!p) {
return;
}
var params = {};//augmentObject_(p, );
params.geometry = fromOverlaysToJSON_(p.geometry);
params.geometryType = getGeometryType_(p.geometry);
params.mapExtent = fromOverlaysToJSON_(p.bounds);
params.tolerance = p.tolerance || 2;
params.sr = 4326;
params.imageDisplay = '' + p.width + ',' + p.height + ',' + (p.dpi || 96);
params.layers = (p.layerOption || 'all');
if (p.layerIds) {
params.layers += ':' + p.layerIds.join(',');
}
if (p.layerDefs) {
params.layerDefs = getLayerDefsString_(p.layerDefs);
}
params.maxAllowableOffset = p.maxAllowableOffset;
params.returnGeometry = (p.returnGeometry === false ? false : true);
getJSON_(this.url + '/identify', params, '', function (json) {
// process results;
var rets = null;
var i, js, g;
if (json.results) {
rets = [];
for (i = 0; i < json.results.length; i++) {
js = json.results[i];
g = fromJSONToOverlays_(js.geometry, p.overlayOptions);
js.feature = {
geometry: g,
attributes: js.attributes
};
delete js.attributes;
}
}
callback(json);
handleErr_(errback, json);
});
};
/**
* @name FindOptions
* @class This class represent the parameters needed in an find operation for a {@link MapService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/find.html'>Find Operation</a>.
* @property {String} [searchText] The search string. This is the text that is searched across the layers and the fields that the user specifies.
* @property {Boolean} [contains = true] If false, the operation searches for an exact match of
* the searchText string. An exact match is case sensitive.
* Otherwise, it searches for a value that contains the searchText provided.
* This search is not case sensitive. The default is true.
* @property {Array.string} [searchFields] The names of the fields to search.
* If this parameter is not specified, all fields are searched.
* @property {Array.number} [layerIds] The layer Ids to perform the find operation on. The layers to perform the find operation on.
* @property {Boolean} [returnGeometry = true] If true, the resultset will include the geometries associated with each result.
* @property {Number} [maxAllowableOffset] This option can be used to specify the maximum allowable offset to be used for generalizing
* geometries returned by the find operation
*/
/**
* @name FindResults
* @class This class represent the results of a find operation for a {@link MapService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/find.html'>Find Operation</a>.
* @property {Array.FindResult} [results] The find results as an array of {@link FindResult}
*/
/**
* @name FindResult
* @class This class represent one entry in the results of a find operation for a {@link MapService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/find.html'>Find Operation</a>.
* @property {Number} [layerId] layerId
* @property {String} [layerName] layerName
* @property {String} [value] value of the display field
* @property {String} [displayFieldName] displayFieldName
* @property {String} [foundFieldName] foundFieldName
* @property {String} [geometryType] esriGeometryPoint | esriGeometryPolyline | esriGeometryPolygon | esriGeometryEnvelope
* @property {Feature} [feature] {@link Feature}
*/
/**
* Find features using the {@link FindOptions} and process {@link FindResults}
* using the <code>callback</code> function.
* For more info see <a
* href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/find.html'>Find Operation</a>.
* @param {FindOptions} opts
* @param {Function} callback
* @param {Function} errback
*/
MapService.prototype.find = function (opts, callback, errback) {
if (!opts) {
return;
}
// handle searchText, contains, maxAllowableOffset
var params = augmentObject_(opts, {});
if (opts.layerIds) {
params.layers = opts.layerIds.join(',');
delete params.layerIds;
}
if (opts.searchFields) {
params.searchFields = opts.searchFields.join(',');
}
params.contains = (opts.contains === false ? false : true);
if (opts.layerDefinitions) {
params.layerDefs = getLayerDefsString_(opts.layerDefinitions);
delete params.layerDefinitions;
}
params.sr = 4326;
params.returnGeometry = (opts.returnGeometry === false ? false : true);
getJSON_(this.url + '/find', params, '', function (json) {
var rets = null;
var i, js, g;
if (json.results) {
rets = [];
for (i = 0; i < json.results.length; i++) {
js = json.results[i];
g = fromJSONToOverlays_(js.geometry, opts.overlayOptions);
js.feature = {
'geometry': g,
'attributes': js.attributes
};
delete js.attributes;
}
}
callback(json);
handleErr_(errback, json);
});
};
/**
* Query a layer with given id or name using the {@link QueryOptions} and process {@link ResultSet}
* using the <code>callback</code> function.
* See {@link Layer}.
* For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/query.html'>Query Layer Operation</a>.
* @param {Number|String} layerNameOrId
* @param {QueryOptions} params
* @param {Function} callback
* @param {Function} errback
*/
MapService.prototype.queryLayer = function (layerNameOrId, params, callback, errback) {
var layer = this.getLayer(layerNameOrId);
if (layer) {
layer.query(params, callback, errback);
}
};
/**
* Creates a GeocodeService class.
* Params:<li><code>url</code>: URL of service, syntax:<code> http://{catalogurl}/{serviceName}/GeocodeServer</code>
* @name GeocodeService
* @class This class (<code>GeocodeService</code>) represent an ArcGIS <a href="http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/geocodeserver.html">GeocodeServer</a>
* service.
* @constructor
* @param {String} url
* @property {String} [serviceDescription] serviceDescription
* @property {Array.Field} [addressFields] input fields.
* Each entry is an object of type {@link Field}, plus <code>required(true|false)</code>
* @property {Array.Field} [candidateFields] candidate Fields.
* Each entry is an object of type {@link Field}
* @property {Array.Field} [intersectionCandidateFields] intersectionCandidateFields
* Each entry is an object of type {@link Field}
* @property {SpatialReference} [spatialReference] spatialReference
* @property {Object} [locatorProperties] an object with key-value pair that is specific to Locator type.
*/
function GeocodeService(url) {
this.url = url;
this.loaded_ = false;
var me = this;
getJSON_(url, {}, '', function (json) {
me.init_(json);
});
}
/**
* init
* @param {Object} json
*/
GeocodeService.prototype.init_ = function (json) {
augmentObject_(json, this);
if (json.spatialReference) {
this.spatialReference = spatialReferences_[json.spatialReference.wkid || json.spatialReference.wkt] || WGS84;
}
this.loaded_ = true;
/**
* This event is fired when the service and it's service info is loaded.
* @name GeocodeService#load
* @event
*/
triggerEvent_(this, 'load');
};
/**
* @name GeocodeOptions
* @class This class represent the parameters needed in a find address candidate operation
* on a {@link GeocodeService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/candidates.html'>Find Adddress Candidates Operation</a>.
* @property {Object} [inputs] an object literal with name-value pair of input values.
* @property {Array.string} [outFields] The list of fields to be included in the returned resultset.
* @property {int|SpatialReference} [outSR] output SR, see {@link SpatialReference}
*/
/**
* @name GeocodeResults
* @class This class represent the results of an find address candidate operation for a
* {@link GeocodeService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/candidates.html'>Find Adddress Candidates Operation</a>.
* @property {Array.GeocodeResult} [candidates] The find address results as
* an array of {@link GeocodeResult}
*/
/**
* @name GeocodeResult
* @class This class represent one entry in the results of a find address operation for a
* {@link GeocodeService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/candidates.html'>Find Adddress Candidates Operation</a>.
* @property {String} [address] matched address
* @property {google.maps.LatLng} [location] matched location
* @property {Number} [score] matching score
* @property {Object} [attributes] attributes as name-value JSON object.
*/
/**
* The findAddressCandidates operation is performed on a geocode service
* resource. The result of this operation is a resource representing
* the list of address candidates. This resource provides information
* about candidates including the address, location, and score.
* param is an instance of {@link GeocodeOptions}. An instance of
* {@link GeocodeResults} will be passed into callback function.
* @param {GeocodeOptions} params
* @param {Function} callback
* @param {Function} errback
*/
GeocodeService.prototype.findAddressCandidates = function (gparams, callback, errback) {
var params = augmentObject_(gparams, {});
if (params.inputs) {
augmentObject_(params.inputs, params);
delete params.inputs;
}
if (isArray_(params.outFields)) {
params.outFields = params.outFields.join(',');
}
//params.outSR = 4326;
var me = this;
getJSON_(this.url + '/findAddressCandidates', params, '', function (json) {
if (json.candidates) {
var res, loc;
var cands = [];
for (var i = 0; i < json.candidates.length; i++) {
res = json.candidates[i];
loc = res.location;
if (!isNaN(loc.x) && !isNaN(loc.y)) {
var ll = [loc.x, loc.y];
// problem: AGS9.31 does not support outSR, so it wil be ignored.
// however 10.0 does not return wkid in the result.
// as compromise, use outSR in 10's request, not included in 9.3.
var sr = me.spatialReference;
if (gparams.outSR){
sr = spatialReferences_[gparams.outSR];
}
if (sr) ll = sr.inverse(ll);
res.location = new G.LatLng(ll[1], ll[0]);
cands[cands.length] = res;
}
}
}
callback({
candidates:cands
});
handleErr_(errback, json);
});
};
/**
* Alias of <code>GeocodeService.findAddressCandidates</code>;
* @param {GeocodeOptions} params
* @param {Function} callback
*/
GeocodeService.prototype.geocode = function (params, callback) {
this.findAddressCandidates(params, callback);
};
/**
* @name ReverseGeocodeOptions
* @class This class represent the parameters needed in a reverseGeocode operation
* on a {@link GeocodeService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/inverse.html'>Reverse Geocode Operation</a>.
* @property {google.maps.LatLng} [location] an object literal of LatLng.
* @property {Number} [distance] The distance in meters from the given location within which
* a matching address should be searched.
*/
/**
* @name ReverseGeocodeResult
* @class This class represent one entry in the results of a find address operation for a
* {@link GeocodeService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/inverse.html'>Reverse Geocode Operation</a>.
* @property {Object} [address] matched address, object literal with name-value address parts.
* @property {google.maps.LatLng} [location] matched location
*/
/**
* The reverseGeocode operation is The reverseGeocode operation is performed on a geocode service resource.
* The result of this operation is a inverse geocoded address resource.
* param is an instance of {@link ReverseGeocodeOptions}. An instance of
* {@link ReverseGeocodeResult} will be passed into callback function.
* @param {ReverseGeocodeOptions} params
* @param {Function} callback
* @param {Function} errback
*/
GeocodeService.prototype.reverseGeocode = function (params, callback, errback) {
if (!isString_(params.location)) {
params.location = fromOverlaysToJSON_(params.location);
}
params.outSR = 4326;
var me = this;
getJSON_(this.url + '/reverseGeocode', params, '', function (json) {
if (json.location) {
var loc = json.location;
if (!isNaN(loc.x) && !isNaN(loc.y)) {
var ll = [loc.x, loc.y];
if (me.spatialReference) {
ll = me.spatialReference.inverse(ll);
}
json.location = new G.LatLng(ll[1], ll[0]);
}
}
callback(json);
handleErr_(errback, json);
});
};
//TODO: implement more Geometry operations
/**
* Creates an GeometryService class.
* Params:<li><code>url</code>: URL of service, syntax:<code> http://{catalogurl}/{serviceName}/GeometryServer</code>
* @name GeometryService
* @constructor
* @class This class (<code>GeometryService</code>) represent an ArcGIS
* <a href="http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/geometryserver.html">Geometry</a>
* service.
* @param {String} url
*/
function GeometryService(url) {
this.url = url;
this.t = 'geocodeservice';
}
/**
* @name ProjectOptions
* @class This class represent the parameters needed in an project operation
* for a {@link GeometryService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/project.html'>Project Operation</a>.
* @property {Array.OverlayView|Array.object} [geometries] Array of <code>google.maps.LatLng, Polyline, Polygon</code>, or ESRI Geometry format to project.
* @property {GeometryType} [geometryType] esriGeometryPoint | esriGeometryPolyline | esriGeometryPolygon | esriGeometryEnvelope
* @property {SpatialReference} [inSpatialReference] The well-known ID of or the {@link SpatialReference} of the input geometries
* @property {SpatialReference} [outSpatialReference] The well-known ID of or the {@link SpatialReference} of the out geometries
*/
/**
* @name ProjectResults
* @class This class represent the parameters needed in an project operation
* for a {@link GeometryService}.
* There is no constructor, use JavaScript object literal.
* <br/>For more info see <a href = 'http://sampleserver3.arcgisonline.com/ArcGIS/SDK/REST/project.html'>Project Operation</a>.
* @property {Array.OverlayView|Array.object} [geometries] Array of <code>google.maps.LatLng, Polyline, Polygon<code>, or ESRI Geometry format to project.
*/
/**
* This resource projects an array of input geometries from an input spatial reference
* to an output spatial reference. Result of type {@link ProjectResults} is passed in callback function.
* @param {ProjectOptions} params
* @param {Function} callback
* @param {Function} errback
*/
GeometryService.prototype.project = function (p, callback, errback) {
var params = prepareGeometryParams_(p);
getJSON_(this.url + '/project', params, "callback", function (json) {
var geom = [];
if (p.outSpatialReference === 4326 || p.outSpatialReference.wkid === 4326) {
for (var i = 0, c = json.geometries.length; i < c; i++) {
geom.push(fromJSONToOverlays_(json.geometries[i]));
}
json.geometries = geom;
}
callback(json);
handleErr_(errback, json);
});
};
/**
* Common units code in spatialReferences. Used in buffer operation.
* This only has the most common units, for a full list of supported units, see
* <a href=http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriGeometry/esriSRUnitType.htm>esriSRUnitType</a>
* and <a href=http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriGeometry/esriSRUnit2Type.htm>esriSRUnit2Type</a>
* @enum {Number}
* @property {Number} [METER] 9001 International meter.
* @property {Number} [FOOT] 9002 International meter.
* @property {Number} [SURVEY_FOOT] 9003 US survey foot.
* @property {Number} [SURVEY_MILE] 9035 US survey mile.
* @property {Number} [KILLOMETER] 9036 killometer.
* @property {Number} [RADIAN] 9101 radian.
* @property {Number} [DEGREE] 9102 degree.
*/
var SRUnit = {
METER: 9001,
FOOT: 9002,
SURVEY_FOOT: 9003,
SURVEY_MILE: 9035,
KILLOMETER: 9036,
RADIAN: 9101,
DEGREE: 9102
};
/**
* @name BufferOptions
* @class This class represent the parameters needed in an buffer operation
* for a {@link GeometryService}.
* @property {Array.OverlayView|Array.object} [geometries] Array of <code>google.maps.LatLng</code>, <code>Polyline</code>, <code>Polygon</code>, or ESRI Geometry format to buffer.
* @property {SpatialReference} [bufferSpatialReference] The well-known ID of or the {@link SpatialReference} of the buffer geometries
* @property {Array.number} [distances] The distances the input geometries are buffered.
* @property {Number} [unit] see <a href='http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriGeometry/esriSRUnitType.htm'>esriSRUnitType Constants </a> .
* @property {Boolean} [unionResults] If true, all geometries buffered at a given distance are unioned into a single (possibly multipart) polygon, and the unioned geometry is placed in the output array.
* @property {OverlayOptions} [overlayOptions] how to render result overlay. See {@link OverlayOptions}
*/
/**
* @name BufferResults
* @class This class represent the parameters needed in an project operation
* for a {@link GeometryService}.
* There is no constructor, use JavaScript object literal.
* @property {Array.OverlayView|Array.object} [geometries] Array of <code>google.maps.LatLng, Polyline, Polygon</code>, or ESRI Geometry format to project.
*/
/**
* This resource projects an array of input geometries from an input spatial reference
* to an output spatial reference. Result of type {@link BufferResults} is passed in callback function.
* @param {BufferOptions} params
* @param {Function} callback.
* @param {Function} errback
*/
GeometryService.prototype.buffer = function (p, callback, errback) {
var params = prepareGeometryParams_(p);
if (p.bufferSpatialReference) {
params.bufferSR = formatSRParam_(p.bufferSpatialReference);
}
params.outSR = 4326;
params.distances = p.distances.join(',');
if (p.unit) {
params.unit = p.unit;
}
getJSON_(this.url + '/buffer', params, "callback", function (json) {
var geom = [];
if (json.geometries) {
for (var i = 0, c = json.geometries.length; i < c; i++) {
geom.push(fromJSONToOverlays_(json.geometries[i], p['overlayOptions']));
}
}
json.geometries = geom;
callback(json);
handleErr_(errback, json);
});
};
/**
* @name GPService
* @class GPService
* @constructor
* @property {String} [serviceDescription]
* @property {Array.string} [tasks]
* @property {String} [executionType]
* @property {String} [resultMapServerName]
* @param {String} url http://[catalog-url]/[serviceName]/GPServer
*/
function GPService(url) {
this.url = url;
this.loaded_ = false;
var me = this;
getJSON_(url, {}, '', function (json) {
augmentObject_(json, me);
me.loaded_ = true;
/**
* This event is fired when the service and it's service info is loaded.
* @name GPService#load
* @event
*/
triggerEvent_(me, 'load');
});
}
/**
* @name GPParameter
* @property {String} [name]
* @property {String} [dataType]
* @property {String} [displayName]
* @property {String} [direction]
* @property {Object} [defaultValue]
* @property {Object} [parameterType]
* @property {String} [category]
* @property {Array.object} [choiceList]
*/
/**
* @name GPTask
* @class GPTask
* @constructor
* @property {String} [name]
* @property {String} [displayName]
* @property {String} [category]
* @property {String} [helpUrl]
* @property {String} [executionType]
* @property {Array.GPParameter} [parameters] see {@link GPParameter}
* @property {String} [name]
* @property {String} [name]
* @property {Array.string} [tasks]
* @property {String} [resultMapServerName]
* @param {String} url http://[catalog-url]/[serviceName]/GPServer
*/
function GPTask(url) {
this.url = url;
this.loaded_ = false;
var me = this;
getJSON_(url, {}, '', function (json) {
augmentObject_(json, me);
me.loaded_ = true;
/**
* This event is fired when the service and it's service info is loaded.
* @name GPService#load
* @event
*/
triggerEvent_(me, 'load');
});
}
/**
* @name GPOptions
* @property {Object} [parameters] name-value pair of params.
* @property {Number|SpatialReference} [outSpatialReference]
* @property {Number|SpatialReference} [processSpatialReference]
*/
/**
* execute a GeoProcessing task
* @param {GPOptions} p
* @param {Function} callback will pass {@link GPResults}
* @param {Function} errback pass in {@link Error}
*/
GPTask.prototype.execute = function (p, callback, errback) {
var params = {};
if (p.parameters) {
augmentObject_(p.parameters, params);
}
if (p.outSpatialReference) {
params['env:outSR'] = formatSRParam_(p.outSpatialReference);
} else {
params['env:outSR'] = 4326;
}
if (p.processSpatialReference) {
params['env:processSR'] = formatSRParam_(p.processSpatialReference);
}
getJSON_(this.url + '/execute', params, '', function (json) {
if (json.results) {
var res, f;
for (var i = 0; i < json.results.length; i++) {
res = json.results[i];
if (res.dataType === 'GPFeatureRecordSetLayer') {
for (var j = 0, J = res.value.features.length; j < J; j++) {
f = res.value.features[j];
if (f.geometry) {
f.geometry = fromJSONToOverlays_(f.geometry, p.overlayOptions);
}
}
}
}
}
callback(json);
handleErr_(errback, json);
});
};
/**
* @name GPResults
* @property {Array.string} messages
* @property {Array.GPResult} results
*/
/**
* @name GPResult
* @property {String} paramName
* @property {String} dataType
* @property {Object} value
*/
/**
* @name NetworkService
* @class NetworkService
* @constructor
* @property {String} serviceDescription
* @property {Array.string} routeLayers
* @property {Array.string} serviceAreaLayers
* @property {Array.string} closestFacilityLayers
* @param {String} url http://[catalog-url]/[serviceName]/NAServer
*/
function NetworkService(url) {
this.url = url;
this.loaded_ = false;
var me = this;
getJSON_(url, {}, '', function(json) {
augmentObject_(json, me);
me.loaded_ = true;
/**
* This event is fired when the service and it's service info is loaded.
* @name NetworkService#load
* @event
*/
triggerEvent_(me, 'load');
});
}
/**
* @name RouteOptions
* @class intance that specify how a route should be solved.
* @property {Array.google.maps.LatLng|Array.Marker} [stops] the locations the route must pass
* @property {Array.google.maps.LatLng|Array.Marker} [barriers] the locations the route must avoid
* @property {Boolean} [returnDirections] If true, directions will be generated and returned with the analysis results. Default is true
* @property {Boolean} [returnRoutes] If true, routes will be returned with the analysis results. Default is true.
* @property {Boolean} [findBestSequence] If true, the solver should resequence the route in the optimal order. The default is as defined in the network layer.
* @property {Boolean} [preserveFirstStop] If true, the solver should resequence the route in the optimal order. The default is as defined in the network layer.
* @property {Boolean} [preserveLastStop] If true, the solver should resequence the route in the optimal order. The default is as defined in the network layer.
*/
/**
* @name RouteResults
* @class intance that specify the results of the solve operation.
* @property {Array.google.maps.LatLng} [stops]
*/
/**
* Create a route task with the URL of the routing server resource.
* @name RouteTask
* @class This class (<code>RouteTask</code>) represent a Network Layer resource deployed in a NetWorkService.
* It can solve a route based on stops, barrier
* @constructor
* @param {String} url
*/
function RouteTask(url) {
this.url = url;
}
/**
* Solve a route based on inputs such as stops and barriers. Result of type {@link RouteResults}
* is passed to Function callback, and error of type {@link Error} is passed to Function errback.
* @param {RouteOptions} opt_Route
* @param {Function} callback
* @param {Function} errback
*/
RouteTask.prototype.solve = function (opts, callback, errback) {
if (!opts) {
return;
}
// handle many other fields
var params = augmentObject_(opts, {});
//params['outSR'] = WGS84.wkid;
if (isArray_(opts.stops)) {
params.stops = fromLatLngsToFeatureSet_(opts.stops);
}
if (isArray_(opts.barriers)) {
if (opts.barriers.length > 0) {
params.barriers = fromLatLngsToFeatureSet_(opts.barriers);
} else {
delete params.barriers;
}
}
params.returnRoutes = (opts.returnRoutes === false ? false : true);
params.returnDirections = (opts.returnDirections === true ? true : false);
params.returnBarriers = (opts.returnBarriers === true ? true : false);
params.returnStops = (opts.returnStops === true ? true : false);
getJSON_(this.url + '/solve', params, '', function (json) {
if (json.routes) {
parseFeatures_(json.routes.features, opts.overlayOptions);
}
callback(json);
handleErr_(errback, json);
});
};
/**
* @name OverlayOptions
* @class Instance of this classes that specify how
* the geometry features returned by ArcGIS server should be rendered in the browser.
* @property {google.maps.MarkerOptions} [markerOptions] style option for points.
* @property {google.maps.PolylineOptions} [polylineOptions] style option for polylines. <a href=http://code.google.com/apis/maps/documentation/javascript/reference.html#PolylineOptions>PolylineOptions</a>
* @property {google.maps.PolygonOptions} [polygonOptions] style option for polygons. <a href=http://code.google.com/apis/maps/documentation/javascript/reference.html#PolygonOptions>PolygonOptions</a>
* @property {Number} [strokeOpacity] The stroke opacity between 0.0 and 1.0
* @property {Number} [fillOpacity] The fill opacity between 0.0 and 1.0
* @property {String} [strokeColor] The stroke color in HTML hex style, ie. "#FFAA00"
* @property {String} [fillColor] The fill color in HTML hex style, ie. "#FFAA00"
* @property {Number} [strokeWeight] The stroke width in pixels.
* @property {Number} [zIndex] The zIndex compared to other overlays.
* @property {String|google.maps.MarkerImage} [icon] Icon for the foreground
* @property {String|google.maps.MarkerImage} [shadow] Shadow image
*/
/**
* @name TileInfo
* @class This class contains information about map tile infornation for a cached map service.
* <br/>There is no constructor for this class.
* @property {Number} [rows] tile row size, e.g. 512, must be same as cols
* @property {Number} [cols] tile cols size, e.g. 512, must be same as rows
* @property {Number} [dpi] dot per inch for map tiles.
* @property {String} [format] PNG8 | PNG24 | PNG32 | GIF | JPEG
* @property {Number} [compressionQuality] JPEG only.0-100.
* @property {Point} [origin] origin of tile system of type
* @property {SpatialReference} [spatialReference] spatial reference. <b>wkid info only</b>.
* @property {Array.LOD} [lods] Array of Level of Details. See {@link LOD}
*/
/**
* @name LOD
* @class This class contains information about one "Level Of Detail" for a cached map service.
* It is the type of {@link lods} property of {@link TileInfo}
* <br/>There is no constructor for this class. Use as object literal.
* @property {Number} [level] zoom level.
* @property {Number} [resolution] map unit per pixel
* @property {Number} [scale] actual map scale. e.g a value of 5000 means 1:5000 scale.
*/
/**
* Creates an ArcGIS Map Tiling Reference System.
* <ul>
* <li><code>tileInfo</code> tiling information. An instance of {@link TileInfo}
* </ul>Applications normally do not create instances of this class directly.
* @name Projection
* @implements {google.maps.Projection}
* @constructor
* @class This class (<code>Projection</code>) implements a custom
* <a href = 'http://code.google.com/apis/maps/documentation/javascript/reference.html#Projection'>google.maps.Projection</a>
* from the core Google Maps API.
* It includes a real {@link SpatialReference} object to convert LatLng from/to
* map coordinates, and tiling scheme informations to convert
* map coordinates from/to pixel coordinates.
* @param {TileInfo} tileInfo
*/
function Projection(tileInfo) {
//if (!tileInfo) {
// throw new Error('map service is not tiled');
//}
this.lods_ = tileInfo ? tileInfo.lods : null;
this.spatialReference_ = tileInfo ? spatialReferences_[tileInfo.spatialReference.wkid || tileInfo.spatialReference.wkt] : WEB_MERCATOR;
if (!this.spatialReference_) {
throw new Error('unsupported Spatial Reference');
}
// resolution (unit/pixel) at lod level 0. Due to changes from V2-V3,
// zoom is no longer defined in Projection. It is assumed that level's zoom factor is 2.
this.resolution0_ = tileInfo ? tileInfo.lods[0].resolution : 156543.033928;
// zoom offset of this tileinfo's zoom 0 to Google's zoom0
this.minZoom = Math.floor(Math.log(this.spatialReference_.getCircum() / this.resolution0_ / 256) / Math.LN2 + 0.5);
this.maxZoom = tileInfo ? this.minZoom + this.lods_.length - 1 : 20;
if (G.Size) {
this.tileSize_ = tileInfo ? new G.Size(tileInfo.cols, tileInfo.rows) : new G.Size(256, 256);
}
// Find out how the map units scaled to 1 tile at zoom 0.
// from V2-V3, coords must scaled to 256 pixel under Mercator at zoom 0.
// scale can be considered under this SR, what's the actual pixel number to 256 to cover whole earth?
this.scale_ = Math.pow(2, this.minZoom) * this.resolution0_;
this.originX_ = tileInfo ? tileInfo.origin.x : -20037508.342787;
this.originY_ = tileInfo ? tileInfo.origin.y : 20037508.342787;
// validation check
if (tileInfo) {
var ratio;
for (var i = 0; i < tileInfo.lods.length - 1; i++) {
ratio = tileInfo.lods[i].resolution / tileInfo.lods[i + 1].resolution;
if (ratio > 2.001 || ratio < 1.999) {
throw new Error('This type of map cache is not supported in V3. \nScale ratio between zoom levels must be 2');
}
}
}
}
/**
* See <a href = 'http://code.google.com/apis/maps/documentation/javascript/reference.html#Projection'>google.maps.Projection</a>.
* @param {LatLng} gLatLng
* @param {Point} opt_point
* @return {Point} pixel
*/
Projection.prototype.fromLatLngToPoint = function (latlng, opt_point) {
if (!latlng || isNaN(latlng.lat()) || isNaN(latlng.lng())) {
return null;
}
var coords = this.spatialReference_.forward([latlng.lng(), latlng.lat()]);
var point = opt_point || new G.Point(0, 0);
point.x = (coords[0] - this.originX_) / this.scale_;
point.y = (this.originY_ - coords[1]) / this.scale_;
return point;
};
// somehow externs was ignored in adv mode.
Projection.prototype['fromLatLngToPoint'] = Projection.prototype.fromLatLngToPoint;
/**
* See <a href = 'http://code.google.com/apis/maps/documentation/javascript/reference.html#Projection'>google.maps.Projection</a>.
* @param {Point} pixel
* @param {Boolean} opt_nowrap
* @return {LatLng}
*/
Projection.prototype.fromPointToLatLng = function (pixel, opt_nowrap) {
//TODO: handle nowrap
if (pixel === null) {
return null;
}
var x = pixel.x * this.scale_ + this.originX_;
var y = this.originY_ - pixel.y * this.scale_;
var geo = this.spatialReference_.inverse([x, y]);
return new G.LatLng(geo[1], geo[0]);
};
//Projection.prototype['fromLatLngToPoint'] = Projection.prototype.fromLatLngToPoint;
/**
* Get the scale at given level;
* @param {Number} zoom
* @return {Number}
*/
Projection.prototype.getScale = function (zoom) {
var zoomIdx = zoom - this.minZoom;
var res = 0;
if (this.lods_[zoomIdx]) {
res = this.lods_[zoomIdx].scale;
}
return res;
};
Projection.WEB_MECATOR = new Projection();
/**
* @name TileLayerOptions
* @class Instances of this class are used in the {@link opt_layerOpts} argument
* to the constructor of the {@link TileLayer} class.
* @property {String} [hosts] host pattern of tile servers if they are numbered. Most browser
* has default restrictions on how many concurrent connections can be made to
* a single host. One technique to workaround this is to create multiple hosts and rotate them when
* loading tiles.
* The syntax is <code>prefix[<i>numberOfHosts</i>]suffix</code>, for example, <code>"mt[4].google.com"</code> means
* rotate hosts in <code>mt0.google.com, mt1.google.com, mt2.google.com, mt3.google.com</code> (4 hosts).
* @property {Number} [minZoom] min zoom level.
* @property {Number} [maxZoom] max zoom level.
* @property {Number} [opacity] opacity (0-1).
*/
/** Creates a tile layer from a cached by ArcGIS map service.
* <br/> <code> service</code> (required) is the underline {@link MapService}
* <br/> <code>opt_layerOpts</code> (optional) is an instance of {@link TileLayerOptions}.
* @name TileLayer
* @constructor
* @class This class (<code>TileLayer</code>) provides access to a cached ArcGIS Server
* map service. There is no <code>GTileLayer</code> class in Google Maps API V3, this class is kept to allow
* finer control of zoom levels for each individual tile sets within a map type, such as zoom level range and opacity.
* @param {MapService} service
* @param {TileLayerOptions} opt_layerOpts
*/
function TileLayer(service, opt_layerOpts) {
opt_layerOpts = opt_layerOpts || {};
if (opt_layerOpts.opacity) {
this.opacity_ = opt_layerOpts.opacity;
delete opt_layerOpts.opacity;
}
augmentObject_(opt_layerOpts, this);
this.mapService_ = (service instanceof MapService) ? service : new MapService(service);
//In the format of mt[number].domain.com
if (opt_layerOpts.hosts) {
var pro = extractString_(this.mapService_.url, '', '://');
var host = extractString_(this.mapService_.url, '://', '/');
var path = extractString_(this.mapService_.url, pro + '://' + host, '');
this.urlTemplate_ = pro + '://' + opt_layerOpts.hosts + path;
this.numOfHosts_ = parseInt(extractString_(opt_layerOpts.hosts, '[', ']'), 10);
}
this.name = opt_layerOpts.name || this.mapService_.name;
this.maxZoom = opt_layerOpts.maxZoom || 19;
this.minZoom = opt_layerOpts.minZoom || 0;
this.dynaZoom = opt_layerOpts.dynaZoom || this.maxZoom;
if (this.mapService_.loaded_) {
this.init_(opt_layerOpts);
} else {
var me = this;
G.event.addListenerOnce(this.mapService_, 'load', function () {
me.init_(opt_layerOpts);
});
}
this.tiles_ = {};
this.map_ = opt_layerOpts.map;
}
/**
* Initialize the tile layer from a loaded map service
* @param {Object} opt_layerOpts
*/
TileLayer.prototype.init_ = function (opt_layerOpts) {
if (this.mapService_.tileInfo) {
this.projection_ = new Projection(this.mapService_.tileInfo);
this.minZoom = opt_layerOpts.minZoom || this.projection_.minZoom;
this.maxZoom = opt_layerOpts.maxZoom || this.projection_.maxZoom;
}
};
/**
* Returns a string (URL) for given tile coordinate (x, y) and zoom level
* @private not meant to be called by client
* @param {Object} tile
* @param {Number} zoom
* @return {String} url
*/
TileLayer.prototype.getTileUrl = function (tile, zoom) {
var z = zoom - (this.projection_ ? this.projection_.minZoom : this.minZoom);
var url = '';
if (!isNaN(tile.x) && !isNaN(tile.y) && z >= 0 && tile.x >= 0 && tile.y >= 0) {
var u = this.mapService_.url;
if (this.urlTemplate_) {
u = this.urlTemplate_.replace('[' + this.numOfHosts_ + ']', '' + ((tile.y + tile.x) % this.numOfHosts_));
}
var prj = this.projection_ || (this.map_ ? this.map_.getProjection() : Projection.WEB_MECATOR);
if (!prj instanceof Projection) {
// if use Google's image
prj = Projection.WEB_MECATOR;
}
var size = prj.tileSize_;
var numOfTiles = 1 << zoom;
var gworldsw = new G.Point(tile.x * size.width / numOfTiles, (tile.y + 1) * size.height / numOfTiles);
var gworldne = new G.Point((tile.x + 1) * size.width / numOfTiles, tile.y * size.height / numOfTiles);
var bnds = new G.LatLngBounds(prj.fromPointToLatLng(gworldsw), prj.fromPointToLatLng(gworldne));
var fullBounds = this.mapService_.getFullBounds();
if (this.mapService_.singleFusedMapCache === false || zoom > this.dynaZoom) {
// dynamic map service
var params = {
'f': 'image'
};
params.bounds = bnds;
params.format = 'png32';
params.width = size.width;
params.height = size.height;
params.imageSR = prj.spatialReference_;
url = this.mapService_.exportMap(params);
} else if (fullBounds && !fullBounds.intersects(bnds)){
url = '';
} else {
url = u + '/tile/' + z + '/' + tile.y + '/' + tile.x;
}
}
//log_('url=' + url);
return url;
};
/**
* set Opacity
* @param {Number} op (0-1)
*/
TileLayer.prototype.setOpacity = function (op) {
this.opacity_ = op;
var tiles = this.tiles_;
for (var x in tiles) {
if (tiles.hasOwnProperty(x)) {
setNodeOpacity_(tiles[x], op);
}
}
};
/**
* get the opacity (0-1) of the tile layer
* @return {Number}
*/
TileLayer.prototype.getOpacity = function () {
return this.opacity_;
};
/**
* get the underline {@link MapService}
* @return {MapService}
*/
TileLayer.prototype.getMapService = function () {
return this.mapService_;
};
/**
* @name MapTypeOptions
* @class Instance of this class are used in the {@link opt_typeOpts} argument
* to the constructor of the {@link MapType} class. See
* <a href=http://code.google.com/apis/maps/documentation/javascript/reference.html#MapType>google.maps.MapType</a>.
* @property {String} [name] map type name
* @property {Projection} [projection] an instance of {@link Projection}.
* @property {String} [alt] Alt text to display when this MapType's button is hovered over in the MapTypeControl. Optional.
* @property {Number} [maxZoom] The maximum zoom level for the map when displaying this MapType. Required for base MapTypes, ignored for overlay MapTypes.
* @property {Number} [minZoom] The minimum zoom level for the map when displaying this MapType. Optional; defaults to 0.
* @property {google.maps.Size} [tileSize] The dimensions of each tile.
*/
// * @property {Number} [radius] Radius of the planet for the map, in meters. Optional; defaults to Earth's equatorial radius of 6378137 meters.
/**
* Creates a MapType, with a array of {@link TileLayer}s, or a single URL as shortcut.
* @name MapType
* @constructor
* @class This class implements the Google Maps API's
* <a href = http://code.google.com/apis/maps/documentation/javascript/reference.html#MapType>GMapType</a>.
* It holds a list of {@link TileLayer}s.
* <p> Note: all tiled layer in the same map type must use same spatial reference and tile scheme.</p>
* @param {Array.TileLayer|String} tileLayers
* @param {MapTypeOptions} opt_typeOpts
*/
function MapType(tileLayers, opt_typeOpts) {
opt_typeOpts = opt_typeOpts || {};
var i;
if (opt_typeOpts.opacity) {
this.opacity_ = opt_typeOpts.opacity;
delete opt_typeOpts.opacity;
}
augmentObject_(opt_typeOpts, this);
var layers = tileLayers;
if (isString_(tileLayers)) {
layers = [new TileLayer(tileLayers, opt_typeOpts)];
} else if (tileLayers instanceof MapService) {
layers = [new TileLayer(tileLayers, opt_typeOpts)];
} else if (tileLayers instanceof TileLayer) {
layers = [tileLayers];
} else if (tileLayers.length > 0 && isString_(tileLayers[0])) {
layers = [];
for (i = 0; i < tileLayers.length; i++) {
layers[i] = new TileLayer(tileLayers[i], opt_typeOpts);
}
}
this.tileLayers_ = layers;
this.tiles_ = {};
if (opt_typeOpts.maxZoom !== undefined) {
this.maxZoom = opt_typeOpts.maxZoom;
} else {
var maxZ = 0;
for (i = 0; i < layers.length; i++) {
maxZ = Math.max(maxZ, layers[i].maxZoom);
}
this.maxZoom = maxZ;
}
if (layers[0].projection_) {
this.tileSize = layers[0].projection_.tileSize_;
this.projection = layers[0].projection_;
} else {
this.tileSize = new G.Size(256, 256);
}
if (!this.name) {
this.name = layers[0].name;
}
}
/**
* Get a tile for given tile coordinates Returns a tile for the given tile coordinate (x, y) and zoom level.
* This tile will be appended to the given ownerDocument.
* @private not meant to be called directly.
* @param {Point} tileCoord
* @param {Number} zoom
* @return {Node}
*/
MapType.prototype.getTile = function (tileCoord, zoom, ownerDocument) {
var div = ownerDocument.createElement('div');
var tileId = '_' + tileCoord.x + '_' + tileCoord.y + '_' + zoom;
for (var i = 0; i < this.tileLayers_.length; i++) {
var t = this.tileLayers_[i];
if (zoom <= t.maxZoom && zoom >= t.minZoom) {
var url = t.getTileUrl(tileCoord, zoom);
if (url) {
var img = ownerDocument.createElement(document.all ? 'img' : 'div');//IE does not like img
img.style.border = '0px none';
img.style.margin = '0px';
img.style.padding = '0px';
img.style.overflow = 'hidden';
img.style.position = 'absolute';
img.style.top = '0px';
img.style.left = '0px';
img.style.width = '' + this.tileSize.width + 'px';
img.style.height = '' + this.tileSize.height + 'px';
//log_(url);
if (document.all) {
img.src = url;
} else {
img.style.backgroundImage = 'url(' + url + ')';
}
div.appendChild(img);
t.tiles_[tileId] = img;
if (t.opacity_ !== undefined) {
setNodeOpacity_(img, t.opacity_);
} else if (this.opacity_ !== undefined) {
// in FF it's OK to set parent div just once but IE does not like it.
setNodeOpacity_(img, this.opacity_);
}
} else {
// TODO: use a div to display NoData
}
}
}
this.tiles_[tileId] = div;
div.setAttribute('tid', tileId);
return div;
};
MapType.prototype['getTile'] = MapType.prototype.getTile;
/**
* Release tile and cleanup
* @private not meant to be called directly.
* @param {Node} node
*/
MapType.prototype.releaseTile = function (node) {
if (node.getAttribute('tid')) {
var tileId = node.getAttribute('tid');
if (this.tiles_[tileId]) {
delete this.tiles_[tileId];
}
for (var i = 0; i < this.tileLayers_.length; i++) {
var t = this.tileLayers_[i];
if (t.tiles_[tileId]) {
delete t.tiles_[tileId];
}
}
}
};
MapType.prototype['releaseTile'] = MapType.prototype.releaseTile;
/**
* Set Opactity
* @param {Number} op
*/
MapType.prototype.setOpacity = function (op) {
this.opacity_ = op;
var tiles = this.tiles_;
for (var x in tiles) {
if (tiles.hasOwnProperty(x)) {
var nodes = tiles[x].childNodes;
for (var i = 0; i < nodes.length; i++) {
setNodeOpacity_(nodes[i], op);
}
}
}
};
/**
* get opacity
* @return {Number}
*/
MapType.prototype.getOpacity = function () {
return this.opacity_;
};
/**
* get list of {@link TileLayer} in this map type
* @return {Array.TileLayer}
*/
MapType.prototype.getTileLayers = function () {
return this.tileLayers_;
};
/**
* @name MapOverlayOptions
* @class Instance of this class are used in the {@link opt_ovelayOpts} argument
* to the constructor of the {@link MapOverlay} class.
* @property {Number} [opacity = 1.0] Opacity of map image from 0.0 (invisible) to 1.0 (opaque)
* @property {ExportMapOptions} [exportOptions] See {@link ExportMapOptions}
* @property {google.maps.Map} [map] map to attach to.
*/
/**
* Creates an Map Overlay using <code>url</code> of the map service and optional {@link MapOverlayOptions}.
* <li/> <code> service</code> (required) is url of the underline {@link MapService} or the MapService itself.
* <li/> <code>opt_overlayOpts</code> (optional) is an instance of {@link MapOverlayOptions}.
* @name MapOverlay
* @class This class (<code>MapOverlay</code>) extends the Google Maps API's
* <a href = http://code.google.com/apis/maps/documentation/reference.html#OverlayView>OverlayView</a>
* that draws map images from data source on the fly. It is also known as "<b>Dynamic Maps</b>".
* It can be added to the map via <code>setMap(map) </code> method.
* The similar class in the core Map API is <a href = http://code.google.com/apis/maps/documentation/javascript/reference.html#GroundOverlay>google.maps.GroundOverlay</a>,
* however, the instance of this class always cover the viewport exactly, and will redraw itself as map moves.
* @constructor
* @param {String|MapService} service
* @param {MapOverlayOptions} opt_overlayOpts
*/
function MapOverlay(service, opt_overlayOpts) {
opt_overlayOpts = opt_overlayOpts || {};
this.mapService_ = (service instanceof MapService) ? service : new MapService(service);
this.minZoom = opt_overlayOpts.minZoom;
this.maxZoom = opt_overlayOpts.maxZoom;
this.opacity_ = opt_overlayOpts.opacity || 1;
this.exportOptions_ = opt_overlayOpts.exportOptions || {};
this.drawing_ = false;
// do we need another refresh. Normally happens bounds changed before server returns image.
this.needsNewRefresh_ = false;
this.overlay_ = null;
this.div_ = null;
// Once the LatLng and text are set, add the overlay to the map. This will
// trigger a call to panes_changed which should in turn call draw.
if (opt_overlayOpts.map) {
this.setMap(opt_overlayOpts.map);
}
this.map_ = null;
this.listeners_= [];
}
MapOverlay.prototype = new G.OverlayView();
/**
* Called by API not by app code.
* Handler when overlay is added. Interface method.
* This will be called after setMap(map) is called.
*/
MapOverlay.prototype.onAdd = function() {
var me = this;
this.listeners_.push(G.event.addListener(this.getMap(), 'bounds_changed', callback_(this.refresh, this)));
this.listeners_.push(G.event.addListener(this.getMap(), 'dragstart', function(){
me.dragging = true;
}));
this.listeners_.push(G.event.addListener(this.getMap(), 'dragend', function(){
me.dragging = false;
}));
var map = this.getMap();
map.agsOverlays = map.agsOverlays || new G.MVCArray();
map.agsOverlays.push(this);
setCopyrightInfo_(map);
this.map_ = map;
};
MapOverlay.prototype['onAdd'] = MapOverlay.prototype.onAdd;
/**
* Called by API not by app code.
* Handler when overlay is removed.
*/
MapOverlay.prototype.onRemove = function() {
for (var i = 0, j = this.listeners_.length; i < j; i++){
G.event.removeListener(this.listeners_[i]);
}
//G.event.removeListener(this.zoomChangedListener_);
//this.div_.parentNode.removeChild(this.div_);
//this.div_ = null;
if (this.overlay_) this.overlay_.setMap(null);
var map = this.map_;// getMap();
var agsOvs = map.agsOverlays;
if (agsOvs) {
for (var i = 0, c = agsOvs.getLength(); i < c; i++) {
if (agsOvs.getAt(i) == this) {
agsOvs.removeAt(i);
break;
}
}
}
setCopyrightInfo_(map);
this.map_ = null;
};
MapOverlay.prototype['onRemove'] = MapOverlay.prototype.onRemove;
/**
* Called by API not by app code.
* See OverlayView.draw in core API docs.
*/
MapOverlay.prototype.draw = function () {
if (!this.drawing_ || this.needsNewRefresh_ === true) {
this.refresh();
}
};
MapOverlay.prototype['draw'] = MapOverlay.prototype.draw;
/**
* Gets Image Opacity. return <code>opacity</code> between 0-1.
* @return {Number} opacity
*/
MapOverlay.prototype.getOpacity = function () {
return this.opacity_;
};
/**
* Sets Image Opacity. parameter <code>opacity</code> between 0-1.
* @param {Number} opacity
*/
MapOverlay.prototype.setOpacity = function (opacity) {
var op = Math.min(Math.max(opacity, 0), 1);
this.opacity_ = op;
if (this.overlay_) {
setNodeOpacity_(this.overlay_.div_, op);
}
};
/**
* Gets underline {@link MapService}.
* @return {MapService} MapService
*/
MapOverlay.prototype.getMapService = function () {
return this.mapService_;
};
/**
* Refresh the map image in current view port.
*/
MapOverlay.prototype.refresh = function () {
if (this.drawing_ === true) {
this.needsNewRefresh_ = true;
return;
}
var m = this.getMap();
var bnds = m ? m.getBounds() : null;
if (!bnds) {
return;
}
var params = this.exportOptions_;
params.bounds = bnds;
var sr = WEB_MERCATOR;
// V3 no map.getSize()
var s = m.getDiv();
params.width = s.offsetWidth;
params.height = s.offsetHeight;
if (s.offsetWidth == 0 || s.offsetHeight ==0){
return;
}
var prj = m.getProjection(); // note this is not same as this.getProjection which returns MapCanvasProjection
if (prj && prj instanceof Projection) {
sr = prj.spatialReference_;
}
params.imageSR = sr;
/**
* This event is fired before the the drawing request was sent to server.
* @name MapOverlay#drawstart
* @event
*/
triggerEvent_(this, 'drawstart');
var me = this;
this.drawing_ = true;
if (!this.dragging && this.overlay_){
this.overlay_.setMap(null);
this.overlay_ = null;
}
//this.div_.style.backgroundImage = '';
this.mapService_.exportMap(params, function (json) {
me.drawing_ = false;
if (me.needsNewRefresh_ === true) {
me.needsNewRefresh_ = false;
me.refresh();
return;
}
if (json.href) {
if (me.overlay_) {
me.overlay_.setMap(null);
me.overlay_ = null;
}
me.overlay_ = new ImageOverlay(json.bounds, json.href, me.map_, me.opacity_);
}
/**
* This event is fired after the the drawing request was returned by server.
* @name MapOverlay#drawend
* @event
*/
triggerEvent_(me, 'drawend');
});
};
/**
* Check if the overlay is visible, and within zoomzoom range and current map bounds intersects with it's fullbounds.
* @return {Boolean} visible
*/
MapOverlay.prototype.isHidden = function () {
return !(this.visible_ && this.isInZoomRange_());
};
/**
* If this in zoom range
* @private
* @return {Boolean}
*/
MapOverlay.prototype.isInZoomRange_ = function () {
var z = this.getMap().getZoom();
if ((this.minZoom !== undefined && z < this.minZoom) ||
(this.maxZoom !== undefined && z > this.maxZoom)) {
return false;
}
return true;
};
/**
* Makes the overlay visible.
*/
MapOverlay.prototype.show = function () {
this.visible_ = true;
this.div_.style.visibility = 'visible';
this.refresh();
};
/**
* Hide the overlay
*/
MapOverlay.prototype.hide = function () {
this.visible_ = false;
this.div_.style.visibility = 'hidden';
};
/**
* @class simply an image overaly. Added due to some unknown problems related to
* overlayLayer pane after bounds change since gmaps API v3.4.
* this class is based on sample code USGSOverlay
* @constructor
* @param {Object} bounds
* @param {Object} url
* @param {Object} map
*/
function ImageOverlay(bounds, url, map, op) {
this.bounds_ = bounds;
this.url_ = url;
this.map_ = map;
this.div_ = null;
this.op_ = op;
this.setMap(map);
}
ImageOverlay.prototype = new G.OverlayView();
ImageOverlay.prototype.onAdd = function() {
var div = document.createElement('DIV');
div.style.border = "none";
div.style.borderWidth = "0px";
div.style.position = "absolute";
var s = this.map_.getDiv();
div.style.width = s.offsetWidth + 'px';
div.style.height = s.offsetHeight + 'px';
div.style.backgroundImage = 'url(' + this.url_ + ')';
// Set the overlay's div_ property to this DIV
this.div_ = div;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayImage pane.
var panes = this.getPanes();
setNodeOpacity_(div, this.op_);
panes.overlayLayer.appendChild(div);
};
ImageOverlay.prototype.draw = function() {
// Size and position the overlay. We use a southwest and northeast
// position of the overlay to peg it to the correct position and size.
// We need to retrieve the projection from this overlay to do this.
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels coordinates.
// We'll use these coordinates to resize the DIV.
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
// Resize the image's DIV to fit the indicated dimensions.
var div = this.div_;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
//div.style.width = (ne.x - sw.x) + 'px';
//div.style.height = (sw.y - ne.y) + 'px';
};
ImageOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
/**
* Creates a copyright control
* @name CopyrightControl
* @class put a copyright notice at bottom rigth corner.
* @constructor
* @param {google.maps.Map} map
*/
function CopyrightControl(map){
// reason to put div creation out is allow MapOverlay tigger it if this control is not created.
this.map_ = map;
setCopyrightInfo_(map);
}
/**
* refresh copyright text
*/
CopyrightControl.prototype.refresh = function(){
setCopyrightInfo_(this.map_);
};
gmaps.ags = {
SpatialReference: SpatialReference,
Geographic: Geographic,
LambertConformalConic: LambertConformalConic,
SphereMercator: SphereMercator,
TransverseMercator: TransverseMercator,
SpatialRelationship: SpatialRelationship,
GeometryType: GeometryType,
SRUnit : SRUnit,
Catalog: Catalog,
MapService: MapService,
Layer: Layer,
GeocodeService: GeocodeService,
GeometryService: GeometryService,
GPService: GPService,
GPTask: GPTask,
RouteTask: RouteTask,
Util: Util,
Config: Config,
Projection: Projection,
TileLayer: TileLayer,
MapOverlay: MapOverlay,
MapType: MapType,
CopyrightControl:CopyrightControl
};