/* jshint indent : 4, strict : true, maxparams: 4, maxdepth: 3, eqeqeq: true */
/* jshint forin: true, latedef: true, newcap: true, noarg: true */
/* jshint noempty: true, nonew: true, undef: true, unused: true */
/* jshint trailing: true, quotmark: true, lastsemic: true, browser: true */
/* global Sys, Type, console */
/**
* @ignore
* @preserve Geospatial Portal JavaScript API
* A valid Geospatial Portal SDK license is required for customizing
* Geospatial Portal, GeoMedia WebMap, and/or ERDAS APOLLO.
* A valid product license is required to run each copy of Geospatial Portal,
* GeoMedia WebMap, and/or ERDAS APOLLO that has been customized using the
* Geospatial Portal SDK.
* © 2010-2023 Hexagon AB and/or its subsidiaries and affiliates.
* All Rights Reserved.
*/
(function (window, undefined) {
"use strict";
var T_FUNCTION = "function",
T_NUMBER = "number",
T_STRING = "string",
T_OBJECT = "object",
T_UNDEFINED = "undefined",
T_BOOLEAN = "boolean",
// Fired events
F_SUCCESS = "success",
F_FAILURE = "failure",
// Messages
M_APOLLO_MISSING = "Apollo Catalog not available",
M_ITEM_NOT_SUITABLE = "Item is not suitable for this action",
M_EXTENSION_NOT_PROVIDED = "Extension not provided",
M_APOLLO_ARITY = "Specified input values count does not match process inputs number",
// Portal events
E_USER_AUTHENTICATED = "userAuthenticated",
E_REFRESH_MAP_LEGEND_CONTROL = "refreshMapLegendControl",
E_VISIBILITY_CHANGED = "visibilityChanged",
E_LOCATABILITY_CHANGED = "locatabilityChanged",
E_MAPSERVICE_INITIALIZED = "mapServiceInitialized",
E_MAPSERVICE_INIT_FAILED = "mapServiceInitFailed",
E_USERMAPLIST_CHANGED = "userMapListChanged",
E_MAPRANGE_CHANGED = "mapRangeChanged",
E_SCALE_CHANGED = "scaleChanged",
E_CANCEL_MAP_OPERATION = "cancelMapOperation",
E_CRS_CHANGED = "crsChanged",
E_SHOW_WEBBROWSER = "showWebBrowser",
E_HIDE_WEBBROWSER = "hideWebBrowser",
E_SETTINGS_CHANGED = "settingsChanged",
E_MAPLAYER_PRIORITY_CHANGED = "mapLayerPriorityChanged",
E_PRIORITY_CHANGED = "priorityChanged",
E_SHOW = "show",
E_HIDE = "hide",
E_SHOW_FEATUREINFO = "showFeatureInfo",
E_SHOW_FEATUREINFO_LOADING = "showFeatureInfoLoading",
E_SHOW_FEATUREINFO_ALL_LAYERS = "showFeatureInfoForAllLayers",
E_FEATUREINFO_ALL_FINISHED = "featureInfoForAllLayersFinished",
E_VARIANT_CHANGED = "mapStateVariantChanged",
E_OPACITY_CHANGED = "opacityChanged",
E_PICTOGRAM_CHANGED = "legendItemPictogramChanged",
E_LEGENDITEM_ADDED = "newLegendItemAdded",
E_LEGENDITEM_REMOVED = "legendItemRemoved",
E_LEGENDITEM_VISIBILITY_CHANGED = "visibilityChanged",
E_MAPLAYER_RENDERING = "mapLayerRendering",
E_MAPLAYER_RENDERED = "mapLayerRendered",
E_SELECTEDFEATURES_CHANGED = "selectedFeaturesChanged",
// portal
GP_INTERNAL = "$GP.internal",
P_PLATFORM = "Intergraph.WebSolutions.Core.WebClient.Platform",
P_APOLLO_COMMON = "Intergraph.WebSolutions.Core.SDIPortal.Apollo.Common.Apollo",
P_MAPSERVICE_MANAGER = P_PLATFORM + ".MapServices.MapServiceManager",
P_MAPSTATE_MANAGER = P_PLATFORM + ".MapContent.MapStateManager",
P_UTIL = P_PLATFORM + ".Common.Util",
P_USER_MANAGER = P_PLATFORM + ".User.UserManager",
P_EVENT = P_PLATFORM + ".ClientScript.ClientEventManager",
P_CRS = P_PLATFORM + ".Common.CoordinateReferenceSystemManager",
P_WEB_REQUEST = P_PLATFORM + ".Web.WebRequestWrapper",
P_LOG = P_PLATFORM + ".ClientScript.ClientLogWriter",
P_SETTINGS = P_PLATFORM + ".ClientScript.Settings",
P_REDLINING = P_PLATFORM + ".ClientScript.Draw.Redlinig",
P_MAPRANGE = P_PLATFORM + ".MapContent.MapRange",
P_SIZE = P_PLATFORM + ".Common.Size",
P_FEATURE_DATASET = P_PLATFORM + ".Geodatabase.FeatureDataset",
P_FEATURECLASS = P_PLATFORM + ".Geodatabase.FeatureClass",
P_FEATURE = P_PLATFORM + ".Geodatabase.Feature",
P_POINT = P_PLATFORM + ".ClientScript.Draw.Geometry.Point",
P_CONTROLS = "Intergraph.WebSolutions.Core.WebClient.Controls",
P_SEARCHRESULTACTION_MANAGER = P_CONTROLS + ".SearchResultActionManager",
P_ANALYSIS_MANAGER = P_PLATFORM + ".Data.AnalysisManager",
P_GEOJSON = "$geoJSON",
P_APOLLO = "$apollo",
P_QUIRKS = "$quirks",
P_STYLEBASE = P_PLATFORM + ".Style.StyleBase",
P_SELECTEDFEATURES = P_PLATFORM + ".Data.SelectedFeaturesManager",
P_DSM = P_PLATFORM + ".ClientScript.Draw.DynamicStyleManager",
P_DFEM = P_PLATFORM + ".ClientScript.Draw.DynamicFeatureEventManager",
P_SUPPORT = P_PLATFORM + ".ClientScript.Edit.Support",
P_FIELDCONTAINER = P_PLATFORM + ".Geodatabase.FieldContainer",
P_LID = P_PLATFORM + ".MapServices.LegendItemDefinition",
P_DATAVIEW_IPLUGIN = P_CONTROLS + ".Plugins.IDataViewControlPlugin",
P_DATAVIEW_MANAGER = P_CONTROLS + ".Plugins.DataViewControlManager",
P_SESSION_MANAGER = P_PLATFORM + ".Session.SessionManager",
P_QDI = P_PLATFORM + ".MapServices.QueryDefinitionItem",
P_VSPM = P_PLATFORM + ".Common.VendorSpecificParametersManager",
// namespaces
N_OGC = "http://www.opengis.net/ogc",
// others
portalObjCache = {},
gp,
originalgp,
hasOwnProperty = Object.prototype.hasOwnProperty;
/* HELPER FUNCTIONS */
function warn() {
if (!isSet(console) || typeof console.warn !== T_FUNCTION) return;
console.warn.apply(console, Array.prototype.slice.call(arguments));
}
function isSet(x) {
return typeof x !== T_UNDEFINED;
}
function isAnySet() {
var args = Array.prototype.slice.call(arguments);
for (var i = 0, l = args.length; i < l; i++)
if (isSet(args[i]))
return true;
return false;
}
function isEverySet() {
var args = Array.prototype.slice.call(arguments);
for (var i = 0, l = args.length; i < l; i++)
if (!isSet(args[i]))
return false;
return true;
}
// http://www.ietf.org/rfc/rfc4122.txt
function createUuid () {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
function getPortalObj(name) {
if (portalObjCache[name])
return portalObjCache[name];
var chain = name.split("."),
ret = window;
for (var i = 0, l = chain.length; i < l; i++) {
ret = ret[chain[i]];
if (typeof ret === T_UNDEFINED) break;
}
portalObjCache[name] = ret;
return ret;
}
// Finds a MapPublisher MapService in the provided mapServiceManager.
// That's because a MapPublisher MapService is identified by a pair of { url, applicationId }
// instead of just the url, like other MapServices are.
function findMapPublisherMapService(mapServiceManager, config) {
return mapServiceManager.findMapServicesByDefinitionName("MapPublisher").filter(function (svc) {
var svcConfig = svc.get_config();
return svcConfig.url === config.url && svcConfig.applicationId === config.applicationId;
})[0];
}
function alwaysTrue() { return true; }
// Creates findBy function with variable parameters length.
// This findBy function can be used as a predicate
// Each parameter is {key: "key", value: "value"}
// If value is a RegExp, then this RegExp is evaluated instead of ===
// Example: findBy({key: "name", value: "Name1"})
function makeFindByPredicate() {
var args = Array.prototype.slice.call(arguments);
return function (object) {
return args.some(function(kv) {
var value = kv.value,
currentValue = object[kv.key];
return value instanceof RegExp ? value.test(currentValue) : currentValue === value;
});
};
}
// performs membership action on the server and executes the callback
function manageUserProfile(action, username, password, fn) {
function callback(executor) {
if (!getPortalObj(P_UTIL).checkExecutor(executor, getPortalObj(P_UTIL).showError))
return;
var result = executor.get_object();
if (result.error) {
getPortalObj(P_UTIL).showError(result.error);
return;
}
if (result.success)
getPortalObj(P_USER_MANAGER).logIn(username);
else
getPortalObj(P_USER_MANAGER).logIn(null);
getPortalObj(P_EVENT).notify(E_USER_AUTHENTICATED, {}, null);
// TODO: handle dirty exits above
if (typeof fn === T_FUNCTION)
fn({ username: username, result: result });
}
var request = getPortalObj(P_WEB_REQUEST).create({
name: "Membership",
query: { action: action },
body: { login: username, password: password, remember: false },
includeCRS: false,
callback: callback
});
request.invoke();
}
// TODO: copied from SettingsPanel
// TODO: refactor SettingsPanel, it should be in $crs
function updateFitAllRanges(crs) {
if (crs.west >= crs.east || crs.south >= crs.north)
return;
function transformCallback(points) {
if (!points)
return;
var xMin = Math.min(points[0].x, points[1].x);
var xMax = Math.max(points[2].x, points[3].x);
var yMin = Math.min(points[0].y, points[2].y);
var yMax = Math.max(points[1].y, points[3].y);
var newRange = new (getPortalObj(P_MAPRANGE))([xMin, yMin, xMax, yMax]);
var range = getPortalObj(P_CRS).getRange(crs.value);
if (!range) {
getPortalObj(P_CRS).setRange(crs.value, newRange);
}
var mapStates = getPortalObj(P_MAPSTATE_MANAGER).get_mapStates();
/*jshint forin:false */
for (var mapStateId in mapStates) {
if (!hasOwnProperty.call(mapStates, mapStateId))
continue;
var mapState = mapStates[mapStateId];
var fitAllRange = mapState.get_fitAllRange(crs.value);
if (!fitAllRange)
mapState.set_fitAllRange(crs.value, newRange.clone(), { onlyCurrentRange: true });
}
}
getPortalObj(P_CRS).transformPoints(getPortalObj(P_CRS).WGS84Id, crs.value, [
{ x: crs.west, y: crs.south },
{ x: crs.west, y: crs.north },
{ x: crs.east, y: crs.south },
{ x: crs.east, y: crs.north }
], { round: true }, transformCallback, null);
}
// recursively walks through tree structure
// @param {Object} config Configuration options. The list of the available configuration options:
// @param {Function} [config.transformer]
// @param {Function} config.predicate
// @param {String} config.childrenGetter
// @param {Array} config.items
function rangerWalker(config) {
var transformer = config.transformer || function (o) { return o; };
var ret = [];
for (var i = 0, l = config.items.length; i < l; i++) {
if (config.predicate(config.items[i]))
ret.push(transformer(config.items[i]));
ret = ret.concat(rangerWalker({
items: config.items[i][config.childrenGetter]() || [],
predicate: config.predicate,
childrenGetter: config.childrenGetter,
transformer: transformer
}));
}
return ret;
}
function fire(name, fn, args) {
var log = getPortalObj(P_LOG);
log.writeVerbose("Firing [" + name + "]");
if (!isSet(fn) && name === F_FAILURE)
log.writeVerbose("Errback not defined");
if (typeof fn === T_FUNCTION) {
fn.call(null, args);
}
}
function fireEventHandler(name, fn, args) {
var log = getPortalObj(P_LOG);
log.writeVerbose("Firing Event [" + name + "]");
if (!isSet(fn))
log.writeVerbose("Handler not defined");
if (typeof fn === T_FUNCTION) {
fn.call(null, args);
}
}
// recursively determines min and max priorities of given legend item (portal)
function getPriorities(legendItem) {
var p = legendItem._priority,
t = typeof p === T_NUMBER,
ret = [t ? p : +Infinity, t ? p : -Infinity];
var childrenz = legendItem.get_legendItems() || [];
for (var i = 0, l = childrenz.length; i < l; i++) {
var subret = getPriorities(childrenz[i]);
ret = [Math.min(ret[0], subret[0]), Math.max(ret[1], subret[1])];
}
return ret;
}
// returns map state
function getMapState(config) {
var mapStateId = (config || {}).mapStateId || "map";
return getPortalObj(P_MAPSTATE_MANAGER).findMapState(mapStateId);
}
//checks if Apollo Catalog is available
function getApolloMapServiceId() {
var mapServices = getPortalObj(P_MAPSERVICE_MANAGER).findByDefinitionName("Apollo");
if (mapServices.length <= 0)
return null;
return mapServices[0];
}
// returns map layer config of a given legend item (config.legendItem)
function getMapLayerConfig(config) {
if (!config || config.legendItem._parent) return null;
var p = getPriorities(config.legendItem);
var mlc = getMapState(config).get_mapLayerConfigs() || [];
for (var i = 0, l = mlc.length; i < l; i++) {
if (mlc[i]._firstLegendItemPriority === p[0] && mlc[i]._lastLegendItemPriority === p[1])
return mlc[i];
}
return null;
}
// returns map layer for a given map layer config
function getMapLayer(mapLayerConfig) {
if (!mapLayerConfig || typeof mapLayerConfig.get_mapState !== T_FUNCTION ) return null;
var mapLayers = mapLayerConfig.get_mapState().get_mapControl().get_mapLayers();
for (var i = 0, l = mapLayers.length; i < l; i++)
if (mapLayers[i].get_config().get_id() === mapLayerConfig.get_id())
return mapLayers[i];
return null;
}
function findMapLayer(config) {
config = config || {};
var mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
mapLayerName = config.mapLayerName,
mapLayerId = config.mapLayerId,
mapServiceId = config.mapServiceId;
if (!mapControl)
return null;
var mapLayers = mapControl.get_mapLayers() || [];
for (var i = mapLayers.length - 1; i >= 0; i--) {
var mlc = mapLayers[i].get_config();
if (mlc.get_mapService().get_id() === mapServiceId) {
if (isSet(mapLayerName) && mlc.get_name() !== mapLayerName)
continue;
if (isSet(mapLayerId) && mlc.get_id() !== mapLayerId)
continue;
return mapLayers[i];
}
}
return null;
}
function findLegendItemDefinitions(config) {
config = config || {};
var ids = config.ids,
names = config.names,
transformer = config.transformer,
mapServiceId = config.mapServiceId,
mapService = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId),
legendItemDefinitions = mapService.get_legendItemDefinitions() || [],
allowedTypes = config.allowedTypes || [0],
tmpnames = {},
tmpids = {},
i,
l;
for (i = 0, l = (names || []).length; i < l; i++)
tmpnames[names[i]] = 1;
for (i = 0, l = (ids || []).length; i < l; i++)
tmpids[ids[i]] = 1;
var ret = rangerWalker({
items: legendItemDefinitions,
predicate: function (lid) {
// TODO: check what types can be added this way!!!
if (allowedTypes.indexOf(lid.get_type()) === -1) return false;
return (!names && !ids) || tmpids[lid.get_id()] || tmpnames[lid.get_name()];
},
childrenGetter: "get_legendItemDefinitions",
transformer: transformer
});
return ret;
}
// transforms {"foo": "bar"} to [{Key: "foo"}, {Value: "bar"}]
function config2dict(config) {
if (typeof config === T_NUMBER ||
typeof config === T_STRING ||
typeof config === T_BOOLEAN ||
typeof config === T_UNDEFINED) {
return config;
}
var dict = [];
/*jshint forin:false */
for (var pName in config) {
if (!hasOwnProperty.call(config, pName) || typeof config[pName] === T_FUNCTION)
continue;
var value = config[pName];
if (Array.isArray(config[pName]) && config[pName] !== null)
value = config[pName].map(config2dict);
else if (typeof config[pName] === T_OBJECT && config[pName] !== null)
value = config2dict(config[pName]);
dict.push({ Key: pName, Value: value });
}
return dict;
}
// returns UI component
// relies on Sys.Application (Microsoft Ajax stuff)
function getComponent(pattern) {
for (var item in Sys.Application._components) {
if (item.match(pattern))
return Sys.Application._components[item];
}
return null;
}
// shortcut for getting toolbar handle
function getToolbar() {
return getComponent(/MapToolbar$/i);
}
// TODO: keep the getter naming convention
function dump(obj) {
var ret = {};
for (var pName in obj) {
if (pName.match(/^get_/) && typeof obj[pName] === T_FUNCTION)
ret[pName.replace(/^get_/, "")] = obj[pName]();
}
return ret;
}
function apply(config1, config2, defaults, p) {
p = p || alwaysTrue;
if (defaults)
apply(config1, defaults);
if (config1 && config2 && typeof config2 === T_OBJECT)
for (var pName in config2)
if (hasOwnProperty.call(config2, pName) && p(config2, pName))
config1[pName] = config2[pName];
return config1;
}
function copyProperties (dst, src, processors) {
/*jshint forin:false */
for (var pName in src) {
if (!Object.hasOwnProperty.call(src, pName)) continue;
if (processors[pName])
apply(dst, processors[pName]);
else
dst[pName] = src[pName];
}
}
function dig (source, address) {
var p = address.split(".") || [], ret = source;
for (var i = 0, l = p.length; i < l; i++) {
if (!Object.hasOwnProperty.call(ret, p[i]) || !(ret = ret[p[i]]))
return null;
}
return ret;
}
function getXYinScreenResolution (config) {
var mapState = getMapState(config),
mapSize = mapState.get_mapControl().get_mapSize(),
s = mapState.get_mapRange().getSize(),
bl = mapState.get_mapRange().getBottomLeft(),
x = (config.x - bl.x) * mapSize.width / s.width,
y = mapSize.height - (config.y - bl.y) * mapSize.height/s.height;
return {
x: Math.round(x),
y: Math.round(y)
};
}
function getXYinMapResolution (config) {
var mapState = getMapState(config),
mapControl = mapState.get_mapControl();
return {
x: mapControl.get_xInMapResolution(config.x),
y: mapControl.get_yInMapResolution(config.y)
};
}
function quoteIfString(txt) {
if (typeof txt === T_STRING)
return "'" + txt + "'";
else
return txt;
}
function prepareQueryWhereClause( filter, ret) {
ret = ret || "";
var operands = filter.operands || [],
length = operands.length;
for (var i = 0 ; i < length ; i++) {
if (typeof operands[i].operator === T_STRING){
ret = prepareQueryWhereClause(operands[i], ret);
if (i !== length - 1)
ret += " " + filter.operator + " ";
} else {
switch (filter.operator) {
case "AND":
case "OR":
//warning !
break;
case "BETWEEN":
case "NOT BETWEEN":
case "IN":
case "NOT IN":
ret += filter.operands[0] + " " + filter.operator + "(";
ret += operands.slice(1).map(quoteIfString).join(",");
ret += ")";
break;
default:
ret += filter.operands[0] + " " + filter.operator + " " + quoteIfString(filter.operands[1]);
break;
}
break;
}
}
return ret;
}
function prepareWhereAttributes(filter) {
// cloning filter as prepareQuery is mutating
var arg = JSON.parse(JSON.stringify(filter));
return prepareQuery(arg);
}
function isLeaf(obj) {
return !Object.prototype.hasOwnProperty.call(obj, "operator") && !Object.prototype.hasOwnProperty.call(obj, "operands");
}
function wrap(what) {
return [[null, null, null, "("]]
.concat(what)
.concat([
[null, null, null, ")"]
]);
}
function prepareQuery(filter) {
var ret = [],
op = filter.operator,
operator = [
[null, null, null, op]
],
operands = filter.operands || [],
len = operands.length;
if (len === 1) {
ret = ret
.concat(operator)
.concat(prepareQuery(operands[0]));
ret = wrap(ret);
} else if (len === 2) {
if (isLeaf(operands[0]) && isLeaf(operands[1])) {
var value = operands[1];
if (Array.isArray(value))
value = value.join(",");
ret = ret.concat([
[operands[0], op, value, null]
]);
} else {
ret = ret
.concat(prepareQuery(operands[0]))
.concat(operator)
.concat(prepareQuery(operands[1]));
}
ret = wrap(ret);
} else {
var x = prepareQuery({
operator: op,
operands: operands.splice(0, 2)
});
for (var i = 2; i < len; i++) {
x = wrap(x.concat(operator)
.concat(prepareQuery(operands[i - 2])));
}
ret = ret.concat(x);
}
return ret;
}
/*
* creates Custom Feature Info Handler. Parameters documented in
* {@link $GP.events#featureInfo}
* @method
* @return {Function} handler ready to use with $GP.events.featureInfoRequested
* @private
*/
function createFeatureInfoHandler(config) {
var callback = config.handler,
transformargs = config.transformargs,
requestcompleted = config.requestcompleted,
httphandler = config.httphandler,
predicate = config.predicate,
fallback = config.fallback,
geometryFieldNames = config.geometryFieldNames && config.geometryFieldNames.map(function(item) { return item.toLowerCase(); });
return function(eventName, eventArgs, sender) {
getPortalObj(P_MAPSTATE_MANAGER).saveMapStates();
if (eventArgs.legendItemDefinition) {
var lid = eventArgs.legendItemDefinition.get_sourceLegendItemDefinition() || eventArgs.legendItemDefinition;
eventArgs.skipRasterLayers = lid.get_mapService().get_supportsDataEditing() || (typeof eventArgs.featureId !== "undefined" && typeof x === "undefined" && typeof y === "undefined");
eventArgs.mapServiceId = lid.get_mapService().get_id();
eventArgs.legendItemDefinitionKey = lid.getKey();
}
eventArgs.requestEndpoint = httphandler;
// We are retrieving the feature info data from the server to always
// deliver the same structure. Default feature info control has a separate parser
// for the "whole feature"
eventArgs.wholeFeature = null;
if (typeof predicate === T_FUNCTION && !predicate(eventArgs)) {
if (typeof fallback === T_FUNCTION)
fallback(eventName, eventArgs, sender);
return;
}
if (typeof transformargs === T_FUNCTION)
eventArgs = transformargs(eventArgs);
eventArgs.geometryFieldsToSerialize = geometryFieldNames;
if (eventArgs.action === "InsertFeature" && eventArgs.insertFeatureCallback) {
callback({}, eventArgs);
return;
}
var future = getPortalObj(GP_INTERNAL).featureInfoRetriever.getFeatureInfo(eventArgs);
if (typeof requestcompleted === T_FUNCTION) {
future.then(function (result) {
eventArgs.result = result;
if (!requestcompleted(result)) {
fallback(eventName, eventArgs, sender);
} else {
callback(result, eventArgs);
}
});
} else {
future.then(callback);
}
};
}
/*
* creates Custom Feature Info Handler. Parameters documented in
* {@link $GP.events#featureInfo}
* @method
* @return {Function} handler ready to use with $GP.events.featureInfoForAllLayersRequested
* @private
*/
function createFeatureInfoForAllLayersHandler(config) {
var callback = config.handler,
transformargs = config.transformargs,
requestcompleted = config.requestcompleted,
httphandler = config.httphandler,
predicate = config.predicate,
fallback = config.fallback,
geometryFieldNames = config.geometryFieldNames && config.geometryFieldNames.map(function(item) { return item.toLowerCase(); }),
controller = getPortalObj(GP_INTERNAL).featureInfoController;
getPortalObj(P_EVENT).unregister(E_SHOW_FEATUREINFO_LOADING, controller._checkSessionBeforeFeatureInfo, controller);
getPortalObj(P_EVENT).register(E_FEATUREINFO_ALL_FINISHED, function(eventName, result) {
if (typeof requestcompleted === T_FUNCTION) {
var eventArgs = {
result: result
};
if (!requestcompleted(result)) {
fallback(eventName, eventArgs);
} else {
callback(result, eventArgs);
}
} else {
callback(result);
}
});
return function(eventName, eventArgs, sender) {
if (eventArgs.legendItemDefinition) {
var lid = eventArgs.legendItemDefinition.get_sourceLegendItemDefinition() || eventArgs.legendItemDefinition;
eventArgs.skipRasterLayers = lid.get_mapService().get_supportsDataEditing() || (typeof eventArgs.featureId !== "undefined" && typeof x === "undefined" && typeof y === "undefined");
eventArgs.mapServiceId = lid.get_mapService().get_id();
eventArgs.legendItemDefinitionKey = lid.getKey();
}
eventArgs.requestEndpoint = httphandler;
// We are retrieving the feature info data from the server to always
// deliver the same structure. Default feature info control has a separate parser
// for the "whole feature"
eventArgs.wholeFeature = null;
if (typeof predicate === T_FUNCTION && !predicate(eventArgs)) {
if (typeof fallback === T_FUNCTION)
fallback(eventName, eventArgs, sender);
return;
}
if (typeof transformargs === T_FUNCTION)
eventArgs = transformargs(eventArgs);
eventArgs.geometryFieldsToSerialize = geometryFieldNames;
getPortalObj(GP_INTERNAL).featureInfoController._checkSessionBeforeFeatureInfo(eventName, eventArgs, sender);
};
}
/* (We don't want it in the generated documentation as this is private)
* Helper methods that returns portal style object from default style name
* and style stub.
* Aim of this function is not to expose directly portal's style objects
* @param {Object/String} config Configuration options. If it is string, then
* it works as if config.defaultStyleName was passed.
* @param {string} [config.defaultStyleName] Name of one of the portal's default
* styles:
* - default
* - selectRect
* - scale
* - redlining
* - highlight
* - select
* - editing
* - transparent
* @param {string} [config.style] style stub - needs to be documented
* @return portal internal style representation
*/
// TODO: document style stub
// TODO: copy/move doc somewhere else
function getPortalStyle(config) {
config = config || {};
var defaultStyleName,
styleStub = null;
if (typeof config.style === T_STRING) {
defaultStyleName = config.style;
}
else {
styleStub = config.style;
defaultStyleName = config.defaultStyleName || "default";
}
var styleType = config.styleType || P_STYLEBASE;
return new (getPortalObj(styleType))(defaultStyleName, styleStub);
}
// returns portal feature in a callback or an error code if it is not found
function findFeatures(config, callback, errback) {
findFeaturesInCache(config, callback, function () {
queryFeatures(config, callback, errback);
});
}
// convenience call for findFeatures
function findFeature(config, callback, errback) {
findFeatures({
featureIds: [config.featureId],
featureClassId: config.featureClassId,
bbox: config.bbox,
force: config.forceFeatureClassesRefresh
}, function(features) {
fire(F_SUCCESS, callback, features[0]);
}, errback);
}
function findFeaturesInCache(config, callback, errback) {
var featureClassId = config.featureClassId,
legendItemDefinition = getLegendItemDefinition(featureClassId),
mapService = legendItemDefinition.get_mapService(),
mapServiceId = mapService.get_id(),
mapLayers = getMapState(config).get_mapControl().get_mapLayers(),
mapLayer = mapLayers.filter(function (layer) {
var ms = layer.get_config().get_mapService();
return (ms && ms.get_id() === mapServiceId && layer._featureCache && layer._featureCache[featureClassId]);
})[0],
featureIds = config.featureIds,
ret = [],
cache;
if (!mapLayer || !(cache = mapLayer._featureCache[featureClassId]))
return fire(F_FAILURE, errback, { success: false });
/* jshint forin:false*/
for (var pName in cache) {
if (!cache.hasOwnProperty(pName)) continue;
if (!cache[pName].feature) continue;
var feature = cache[pName].feature;
if (featureIds.indexOf(feature.getKey()[0]) > -1)
ret.push(feature);
if (ret.length === featureIds.length)
break;
}
return fire(F_SUCCESS, callback, ret);
}
function createResourceIdNode(value) {
var qdi = new (getPortalObj(P_QDI))("FeatureId", N_OGC);
qdi.get_attributes().fid = value;
return qdi;
}
// currently only the ResourceId operator is optimized
function queryFeatures(config, callback, errback) {
var featureClassId = config.featureClassId,
featureIds = config.featureIds,
legendItemDefinition = getLegendItemDefinition(featureClassId),
mapService = legendItemDefinition.get_mapService(),
bboxPoints = config.bboxPoints,
force = config.forceFeatureClassesRefresh;
var getFeatureDatasetCallback = function (featureDataset) {
if (!featureDataset || featureDataset.error) {
return fire(F_FAILURE, errback, {message: featureDataset || "Unknown error"});
}
var fc = featureDataset.findFeatureClass(featureClassId),
features = fc.get_features() || [],
ret = [];
/* jshint forin:false*/
for (var id in features) {
if (hasOwnProperty.call(features, id) && featureIds.indexOf(features[id].getKey()[0]) > -1) {
ret.push(features[id]);
if (isSet(featureIds) && ret.length === featureIds.length)
break;
}
}
return fire(F_SUCCESS, callback, ret);
};
if (featureIds.length === 1) {
var qdi = createResourceIdNode(featureIds[0]);
mapService.queryFeatures(legendItemDefinition, qdi, getFeatureDatasetCallback);
} else {
mapService.getFeatureDataset([legendItemDefinition], getFeatureDatasetCallback, null, {
bboxPoints: bboxPoints,
force: force
});
}
}
/**
* Legend item - both for leaves and grouping nodes
* @class LegendItem
*/
function LegendItem(config) {
if (!config.portalLegendItem) return;
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.config internal config
* @property {Object} _.li internal portal legendItem representation
* @property {Object} _.mlc internal portal mapLayerConfig representation
* @private
*/
this._ = {
config: config,
li: config.portalLegendItem,
mlc: getMapLayerConfig({
legendItem: config.portalLegendItem,
mapStateId: config.mapStateId
})
};
}
LegendItem.prototype = {
/**
* Fits map range to layer
* @param {Function} callback
* @method fitLayer
*/
fitLayer: function (callback) {
var mapService = this._.li.get_definition().get_mapService(),
mapServiceDefinitionName = this._.li._definitionName || mapService.get_definition().name,
version = mapService.get_config().version;
(getMapLayer(this._.mlc) || this._.li).fitLayer(getPortalObj(P_CRS).getCurrent().get_id(), function (crsId) {
// if layer is WFS analysis, then it already has correct coords
// WMTS and IWS have correct coord too
if (mapServiceDefinitionName !== "WMS") return false;
return getPortalObj(P_CRS).qualifiesForSwapById(crsId, mapServiceDefinitionName, version);
});
fire(F_SUCCESS, callback, { success: true });
},
/**
* Returns priority
* @return {Number} priority
*/
get_priority: function () {
return (this._.mlc || this._.li).get_priority();
},
/**
* Sets new priority
* @param {Number} priority
* @param {Function} [callback] callback executed
* @param {Function} [errback] callback executed if operation fails
*/
set_priority: function (priority, callback, errback) {
var config = this._.config,
eventName,
args = {
mapStateId: getMapState(config).get_id(),
oldPriority: this.get_priority(),
newPriority: priority
},
msg = "Bad priority";
if (this._.mlc) {
eventName = E_MAPLAYER_PRIORITY_CHANGED;
if (priority < 0 || priority > getMapState(config).get_mapLayerConfigs().length) {
fire(F_FAILURE, errback, { success: false, priority: priority, msg: msg });
return;
}
getMapState(config).setMapLayerPriority(this._.mlc, priority);
} else {
eventName = E_PRIORITY_CHANGED;
args.legendItem = this._.li;
args.differLayers = true;
if (priority < 0 || priority > getMapState(config).get_legend().get_flatLegend().length) {
fire(F_FAILURE, errback, { success: false, priority: priority, msg: msg });
return;
}
getMapState(config).setLegendItemPriority(this._.li, priority);
}
getPortalObj(P_EVENT).notify(eventName, args, null);
fire(F_SUCCESS, callback, { success: true, newPriority: priority, legendItem: this });
},
/**
* Removes legend item
* @method remove
* @param {Function} callback
*/
remove: function (callback) {
var items;
if (!this._.mlc)
items = [this._.li];
else
items = rangerWalker({
items: [this._.li],
mapStateId: this._.config.mapStateId,
childrenGetter: "get_legendItems",
predicate: alwaysTrue
});
if (items)
getMapState(this._.config).removeLegendItems(items);
fire(F_SUCCESS, callback, { success: true });
},
/**
* Returns end user visible name of the legend item
* @method get_name
* @return {String}
*/
get_name: function () {
if (this._.mlc)
return this._.mlc.get_name();
return this._.li.get_name() || this._.li.get_definition().get_name();
},
/**
* Sets the end user visible name of the legend item
* @method set_name
* @param {String} name
*/
set_name: function (name) {
(this._.mlc || this._.li).set_name(name);
getPortalObj(P_EVENT).notify(E_REFRESH_MAP_LEGEND_CONTROL, { mapStateId: this._.config.mapStateId || "map" }, null);
},
/**
* Returns whether legend item is visible
* @method get_isVisible
* @return {Boolean}
*/
get_isVisible: function () {
if (!this._.mlc) return this._.li.get_isVisible();
var items = rangerWalker({
items: [this._.li],
mapStateId: this._.config.mapStateId,
childrenGetter: "get_legendItems",
predicate: function (o) {
return o.get_isVisible();
}
});
return items.length > 0;
},
/**
* Sets visibility of the legend item
* @method set_isVisible
* @param {Boolean} isVisible
*/
set_isVisible: function (isVisible) {
var items = rangerWalker({
items: [this._.li],
mapStateId: this._.config.mapStateId,
childrenGetter: "get_legendItems",
predicate: alwaysTrue
});
if (items) {
for (var i = 0, l = items.length; i < l; i++) {
items[i].set_isVisible(isVisible);
}
}
getPortalObj(P_EVENT).notify(E_VISIBILITY_CHANGED, { legendItems: items, mapStateId: this._.config.mapStateId || "map" });
},
/**
* Returns whether legend item is locatable
* @method get_isLocatable
* @return {Boolean}
*/
// TODO: portal logic is strange here...
// on "layer" level, locatability is just "disabled" but all child legendItems remain locatable
get_isLocatable: function () {
if (!this._.mlc) return this._.li.get_isLocatable();
return !this._.mlc.get_isLocatabilityDisabled();
},
/**
* Sets locatability of the legend item
* @method set_isLocatable
* @param {Boolean} isLocatable
*/
set_isLocatable: function (isLocatable) {
if (!this._.mlc) {
this._.li.set_isLocatable(isLocatable);
getPortalObj(P_EVENT).notify(E_LOCATABILITY_CHANGED, { legendItems: [this._.li], mapStateId: this._.config.mapStateId || "map" });
} else getMapLayer(this._.mlc)[isLocatable ? "enableLocatability" : "disableLocatability"]();
},
/**
* Returns opacity. Works only on "layer" nodes. For LegendItem nodes it returns null
* @method get_opacity
* @return {Number?} opacity 0-100
*/
get_opacity: function () {
if (!this._.mlc) return null;
return this._.mlc.get_opacity();
},
/**
* Sets opacity. Works only on "layer" nodes. On LegendItem nodes it does nothing
* @method set_opacity
* @param {Number} opacity new opacity
*/
set_opacity: function (opacity) {
if (!this._.mlc) return;
this._.mlc.set_opacity(opacity);
getPortalObj(P_EVENT).notify(E_OPACITY_CHANGED, { mapLayerConfigId: this._.mlc.get_id() }, null);
},
/**
* Returns pictogram information
* @return {Object} pictogram
* @return {String} pictogram.url URL of the pictogram
* @return {Number} pictogram.width Width of the pictogram in pixels
* @return {Number} pictogram.height Height of the pictogram in pixels
*/
get_pictogram: function() {
if (!this._.li) return null;
return this._.li.get_definition().get_pictogramDefinition();
},
/**
* Updates pictogram
* @param {Object} pictogram Pictogram information
* @param {String} pictogram.url URL of the pictogram
* @param {Number} pictogram.width Width of the pictogram in pixels
* @param {Number} pictogram.height Height of the pictogram in pixels
* @return {void}
*/
set_pictogram: function(pictogram) {
var legendItem = this._.li;
if (!legendItem || !pictogram) return;
legendItem.get_definition().set_pictogramDefinition(pictogram);
getPortalObj(P_EVENT).notify(E_PICTOGRAM_CHANGED, {
legendItem: legendItem,
mapStateId: legendItem.get_legend().get_mapState().get_id()
}, this);
}
};
/**
* @class MapService
* Service representation
*/
function MapService(config) {
if (!config.portalMapService) return;
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.config internal config
* @property {Object} _.ms internal portal mapService representation
* @private
*/
this._ = {
config: config,
ms: config.portalMapService
};
}
MapService.prototype = {
/**
* Removes map service
* @method remove
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
*/
remove: function (callback, errback) {
var mapServiceId = this._.ms.get_id();
getPortalObj(P_MAPSERVICE_MANAGER).removeMapService(mapServiceId, function () {
var success = !getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId);
fire(success ? F_SUCCESS : F_FAILURE, success ? callback : errback, {
success: success,
msId: mapServiceId
});
}, this);
},
/**
* Returns id of the service
* @method get_id
* @return {String}
*/
get_id: function () { return this._.ms.get_id(); },
/**
* Returns end user visible name of the service
* @method get_name
* @return {String}
*/
get_name: function () { return this._.ms.get_name(); },
/**
* Returns definition name of the service
* @method get_definitionName
* @return {String}
*/
get_definitionName: function () { return this._.ms._definitionName; },
/**
* Returns URL of the service
* @method get_url
* @return {String}
*/
get_url: function () { return this._.ms.get_config().url; }
};
// Bogus GeoJSON class to have it in the documentation
/**
* @class GeoJSON
*
* GeoJSON is a format for encoding a variety of geographic data structures.
* A GeoJSON object may represent a geometry, a feature, or a collection of features.
* GeoJSON supports the following geometry types: Point, LineString, Polygon, MultiPoint,
* MultiLineString, MultiPolygon, and GeometryCollection. Features in GeoJSON contain a
* geometry object and additional properties, and a feature collection represents
* a list of features.
* A complete GeoJSON data structure is always an object (in JSON terms).
* In GeoJSON, an object consists of a collection of name/value pairs -- also called
* members. For each member, the name is always a string. Member values are either
* a string, number, object, array or one of the literals: true, false, and null.
* An array consists of elements where each element is a value as described above.
*
* [Full GeoJSON specification](http://geojson.org/geojson-spec.html)
*/
/**
* @class Feature
* Feature representation. Instances of this type are not created from
* the client code, but appear in callbacks of {@link $GP.queries} and
* {@link $GP.selectedFeatures}.
*
* In some circumstances, to prevent unnecessary data transfer,
* these objects are "proxies" to actual features. For this reason it is
* recommended to use callbacks and not returning values.
*
*/
function Feature(config) {
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.config internal config
* @property {Object} _.f internal portal feature representation
* @private
*/
this._ = {
config: config,
f: config.portalFeature,
proxy: {
featureId: config.featureId,
featureClassId: config.featureClassId
}
};
}
Feature.prototype = {
_getPortalFeatureObj: function (callback, errback) {
var scope = this;
findFeature({
featureId: this._.proxy.featureId,
featureClassId: this._.proxy.featureClassId
}, function(portalFeature) {
scope._.f = portalFeature;
callback.call(scope);
}, errback);
},
/**
* Returns a [GeoJSON](http://geojson.org/geojson-spec.html) representation of the feature.
* Since 15.00.0512 Synchronous version of this method:
*
* var obj = feature.get_geoJSON();
* // do something with the GeoJSON object
*
* is not recommended as sometimes features are represented just by their id and featureClassId.
* To be on the safe side, use asynchronous version:
*
* feature.get_geoJSON(obj) {
* // do something with the GeoJSON object
* }
*
* @method get_geoJSON
* @param {Function} callback Callback executed on the resulting GeoJSON object
* @param {GeoJSON} callback.result GeoJSON object
* @param {Function} errback Error callback
* @return {GeoJSON} geojson object. If feature data is not available synchronously
* and there is no callback, then warning appears in the console and
* a bogus GeoJSON object is returned for backward compatibility
*/
get_geoJSON: function (callback, errback) {
if (!this._.f) {
if (!isSet(callback)) warn("Deprecated: get_geoJSON on a proxy feature object without a callback function");
var scope = this;
this._getPortalFeatureObj(function() {
scope.get_geoJSON(callback, errback);
}, errback);
return {
properties: "This get_geoJSON invocation is asynchronous. Use callback to get the data.",
geometry: {coordinates: []}
};
}
if (!this._geoJSON)
this._geoJSON = getPortalObj(P_GEOJSON).getFeature(this._.f);
fire(F_SUCCESS, callback, this._geoJSON);
return this._geoJSON;
},
/**
* Centers the map on the feature centroid
* @param {Function} callback Callback executed after the zoom
* @param {Function} errback Error callback
* @method center
* @return {void}
*/
center: function(callback, errback) {
if (!this._.f) {
var scope = this;
return this._getPortalFeatureObj(function() {
scope.center(callback, errback);
}, errback);
}
var g = this._.f.get_geometry(),
p = getPoint(g);
return gp.map.zoom({ x: p.x, y: p.y }, callback);
},
/**
* Returns feature ID
* @method get_id
* @return {String} ID
*/
get_id: function () {
return this._.proxy.featureId || this._.f.getKey()[0];
},
/**
* Returns featureClassId
* @method get_featureClassId
* @return {String} featureClassId
*/
get_featureClassId: function() {
return this._.proxy.featureClassId || this._.f.get_featureClass().get_id();
}
};
/**
* @class NameSearchResult
* Name search result item
*/
function NameSearchResult(config) {
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.config internal config
* @private
*/
this._ = {
config: config
};
var cpr = config.portalSearchResult;
if (!cpr)
return;
/**
* @property {Point} point Point
* @property {Number} point.x X Coordinate
* @property {Number} point.y Y Coordinate
*/
this.point = {
x: cpr.point[1],
y: cpr.point[0]
};
/**
* @property {String} id ID
*/
this.id = cpr.id;
/**
* @property {String} name Name
*/
this.name = cpr.name;
/**
* @property {Number[]} suggestedBBox Suggested BBOX for setting the map range
*/
this.bbox = cpr.suggestedBBox;
/**
* @property {Number} score Score of the search result. Used in LUWS and OpenLS
*/
this.score = cpr.score;
/**
* @property {String} index Index
*/
this.index = cpr.index;
/**
* @property {String} mapServiceId Search service ID
*/
this.serviceId = cpr.mapServiceId;
/**
* @property {String} mapServiceName Search service name
*/
this.serviceName = cpr.mapServiceName;
}
NameSearchResult.prototype = {
/**
* Centers the map on the point's coordinates
* @method zoom
* @param {Object} [config] Configuration options
* @param {String} [config.mapStateId] ID of a mapState
* @param {Function} [callback] Callback executed on the matched items
* @return {void}
*/
zoom: function(config, callback) {
if (typeof config === T_FUNCTION) {
callback = config;
config = {};
}
var cfg = apply({}, config || {}, {x: this.x, y: this.y});
gp.map.zoom(cfg, callback);
},
/**
* Returns [GeoJSON](http://geojson.org/geojson-spec.html) representation of the search result.
*
* Although this particular method is synchronous, the asynchronous
* invocation is preferred since 15.00.0512 to unify get_geoJSON usage.
* Please see {@link Feature#get_geoJSON} for explanation.
*
* @method get_geoJSON
* @param {Function} callback Callback executed on the resulting GeoJSON object
* @param {GeoJSON} callback.result GeoJSON object
* @param {Function} errback Error callback
* @return {GeoJSON} geojson object.
*/
get_geoJSON: function (callback, errback) {
if (!isSet(errback) && !this.point) {
fire(F_FAILURE, errback, { message: "No data" });
return null;
}
var ret = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [this.point.x, this.point.y]
},
"properties": {
"name": this.name,
"score": this.score,
"bbox": this.bbox,
"id": this.id,
"mapServiceId": this.mapServiceId,
"mapServiceName": this.mapServiceName,
"index": this.index
}
};
if (!isSet(callback)) {
fire(F_SUCCESS, callback, ret);
}
return ret;
}
};
/**
* @deprecated 16.8.0
* @class ApolloSearchResult
* ERDAS APOLLO search result item
*/
function ApolloSearchResult(config) {
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.config internal config
* @private
*/
this._ = {
config: config
};
var cpr = config.portalSearchResult;
if (!cpr)
return;
this._actionMapping = {
wmts: "add_to_map_wmts",
ecwp: "add_to_map_ecwp",
wfs: "add_as_vector",
wms: "add_to_map_wms",
wcs: "add_to_map_wcs" // see WCS\Apollo\WcsApolloPlugin.js
};
/**
* @property {Object} apollo APOLLO specific properties
* @property {Object} apollo.temporalExtent Temporal extent
* @property {Object} apollo.spatialExtent Spatial extent
* @property {Object} apollo.spatialExtent.width Width
* @property {Object} apollo.spatialExtent.height Height
* @property {Object} apollo.spatialExtent.resolutionX Resolution X
* @property {Object} apollo.spatialExtent.resolutionY Resolution Y
*/
this.apollo = {};
copyProperties(this.apollo, cpr, {
temporalExtent: {
temporalExtent: dig(cpr, "temporalExtent.extentValues")
},
spatialExtent: {
width: dig(cpr, "spatialExtent.xExtent.size"),
height: dig(cpr, "spatialExtent.yExtent.size"),
resolutionX: dig(cpr, "spatialExtent.xExtent.resolution.string"),
resolutionY: dig(cpr, "spatialExtent.yExtent.resolution.string")
}
});
}
ApolloSearchResult.prototype = {
_performAction: function (type) {
var actionManager = getPortalObj(P_SEARCHRESULTACTION_MANAGER),
item = this.apollo,
actionName = this._actionMapping[type],
action = actionManager.getAction(actionName);
if(action.predicate({data: item})) {
action.handler([{ data: item, json: item }]);
return true;
}
return false;
},
/**
* Adds current item to map using method specified in config
* @method addToMap
* @param {Object} [config] Configuration options
* @param {String} [config.type] type of a service to use (accepted values: "wms", "wmts", "ecwp", "wfs")
* @param {Function} [callback] Callback executed on the matched items
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
addToMap: function(config, callback, errback) {
config = config || {};
var apolloMapServiceId = getApolloMapServiceId(),
type = config.type || "wms";
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var nfp = this.apollo.nativeFootprint,
fp = this.apollo.footprint;
if(nfp && nfp.envelope && nfp.envelope.length === 5)
nfp.envelope.pop();
if(fp && fp.envelope && fp.envelope.length === 5)
fp.envelope.pop();
if(this._performAction(type))
fire(F_SUCCESS, callback, { success: true, item: this });
else
fire(F_FAILURE, errback, { success: false, message: M_ITEM_NOT_SUITABLE });
},
_zoom: function (result, config, callback, errback) {
var mapState = getMapState(config),
success = false,
p0 = result.points[0],
p1 = result.points[1];
if(result.success) {
if ((p0.x === p1.x) && (p0.y === p1.y))
mapState.centerToPoint(p0.x, p0.y);
else
mapState.setMapRangeByPoints(p0.x, p0.y, p1.x, p1.y);
success = true;
}
fire(success ? F_SUCCESS : F_FAILURE, success ? callback: errback, { success: success});
},
/**
* Zoom the map to the extents of a given Apollo item
* @method zoom
* @param {Object} [config] Configuration options
* @param {String} [config.mapStateId] ID of a mapState
* @param {Function} [callback] Callback executed on the matched items
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
zoom: function(config, callback, errback) {
if (typeof config === T_FUNCTION) {
callback = config;
errback = callback;
config = {};
}
config = config || {};
var message,
extent = {},
sourceSrs = null,
geometries = [],
xmin = Number.POSITIVE_INFINITY,
ymin = Number.POSITIVE_INFINITY,
xmax = Number.NEGATIVE_INFINITY,
ymax = Number.NEGATIVE_INFINITY,
that = this,
z = function(r) {that._zoom(r, config, callback, errback);};
if((!this.apollo || !this.apollo.footprint || !this.apollo.footprint.envelope) && (!this.apollo.nativeFootprint || !this.apollo.nativeFootprint.envelope)) {
message = "No extents available. Zoom not possible.";
fire(F_FAILURE, errback, { success: false, message: message });
return;
}
if(this.apollo.nativeFootprint && this.apollo.nativeFootprint.envelope) {
extent = this.apollo.nativeFootprint.data;
sourceSrs = this.apollo.nativeFootprint.srs;
}
else {
extent = this.apollo.footprint.data;
sourceSrs = this.apollo.footprint.srs;
}
if(typeof(extent[0]) === T_NUMBER)
geometries = [extent];
else if (typeof(extent[0][0]) === T_NUMBER)
geometries = extent;
/*jshint forin:false */
for(var geometry in geometries) {
if(!geometries.hasOwnProperty(geometry)) continue;
for(var j = 0; j <= geometries[geometry].length - 2; j+=2) {
if(geometries[geometry][j] < xmin)
xmin = geometries[geometry][j];
if(geometries[geometry][j+1] < ymin)
ymin = geometries[geometry][j+1];
if(geometries[geometry][j] > xmax)
xmax = geometries[geometry][j];
if(geometries[geometry][j+1] > ymax)
ymax = geometries[geometry][j+1];
}
}
if(gp.crs.getCurrent() !== sourceSrs)
gp.crs.transform({
sourceCrsId: sourceSrs,
targetCrsId: gp.crs.getCurrent(),
points:[{x:xmin, y:ymin}, {x:xmax, y:ymax}]
}, z);
else
z({success: true, points:[{x:xmin, y:ymin}, {x:xmax, y:ymax}]});
},
_ensureAction: function(callback, errback, actionName) {
var apolloMapServiceId = getApolloMapServiceId(),
sram = getPortalObj(P_SEARCHRESULTACTION_MANAGER),
action = sram.getAction(actionName);
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
} else if(!action.predicate({data: this.apollo})) {
fire(F_FAILURE, errback, { success: false, message: M_ITEM_NOT_SUITABLE });
} else {
action.handler([{json: this.apollo}]);
fire(F_SUCCESS, callback, { success: true, item: this });
}
},
/**
* Display the index metadata of a given item
* @method showIndexMetadata
* @param {Function} [callback] Callback executed on the matched items
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
showIndexMetadata: function(callback, errback) {
this._ensureAction(callback, errback, "index_metadata");
},
/**
* Show the ISO metadata of current Apollo item
* @method showISOMetadata
* @param {Function} [callback] Callback executed on the matched items
* @return {void}
*/
showISOMetadata: function(callback, errback) {
this._ensureAction(callback, errback, "iso_metadata");
},
/**
* Download the ISO XML of current Apollo item
* @method downloadISOXML
* @param {Function} [callback] Callback executed on the matched items
* @return {void}
*/
downloadISOXML: function(callback, errback) {
this._ensureAction(callback, errback, "iso_metadata_download");
},
/**
* Download given apollo item from the server using specified format
* @method openIn
* @param {Object} config Configuration options
* @param {Object} config.extension Extension to use.
* @param {Function} [callback] Callback executed on the matched items
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
openIn: function(config, callback, errback) {
var apolloMapServiceId = getApolloMapServiceId(),
allowedClasses = ["com.erdas.rsp.babel.model.imagery.ImageReference", "com.erdas.rsp.babel.model.imagery.Aggregate"],
form = document.createElement("form"),
hiddenField = document.createElement("input"),
extensionField = document.createElement("input"),
mapServiceField = document.createElement("input");
if (!config.extension) {
fire(F_FAILURE, errback, {success: false, message: M_EXTENSION_NOT_PROVIDED});
return;
}
if(allowedClasses.indexOf(this.apollo._class) < 0) {
fire(F_FAILURE, errback, { success: false, message: M_ITEM_NOT_SUITABLE });
return;
}
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
function setField(target, name, value) {
target.setAttribute("type", "hidden");
target.setAttribute("name", name);
target.setAttribute("value", value);
}
form.style.visibility = "hidden";
form.setAttribute("method", "POST");
form.setAttribute("target", "_blank");
form.setAttribute("action", "ApolloSearch.WebClient.ashx?action=openIn");
setField(hiddenField, "jsonData", gp.utils.serialize([this.apollo.id]));
setField(extensionField, "extension", config.extension);
setField(mapServiceField, "mapServiceId", apolloMapServiceId);
form.appendChild(hiddenField);
form.appendChild(extensionField);
form.appendChild(mapServiceField);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
fire(F_SUCCESS, callback, { success: true, item: this });
return;
}
};
function getSwapped(dataset, featureClass) {
var jsonstring = JSON.parse(JSON.stringify(featureClass.serialize())),
fc = new (getPortalObj(P_FEATURECLASS))(dataset, jsonstring),
features = fc.get_features() || {};
/* jshint forin:false */
for (var featureId in features)
if (hasOwnProperty.call(features, featureId)) {
var g = features[featureId].get_geometry();
if (g)
g.swapCoordinates();
}
return fc;
}
function makeDatasetCallback (config, callback, errback) {
return function (dataset) {
if (!dataset) {
fire(F_FAILURE, errback, { success: false, msg: "Data not found." });
return;
}
var gjs = getPortalObj(P_GEOJSON),
result = [],
geoJson,
fcs = dataset.get_featureClasses() || {},
format = config.format || "geojson";
/* jshint forin:false */
for (var c in fcs) {
if (!hasOwnProperty.call(fcs, c)) continue;
var featureClass = fcs[c];
if (config.swapped)
featureClass = getSwapped(dataset, featureClass);
geoJson = gjs.getFeatureCollection(featureClass);
switch(format) {
case "geojson":
result.push(geoJson);
break;
case "featureCollectionsData":
result.push({
geojson: geoJson,
featureClassId: featureClass.get_id(),
featureClassName: featureClass._name || featureClass.get_id()
});
break;
default:
return fire(F_FAILURE, errback, {success: false, msg: "No specified format"});
}
}
fire(F_SUCCESS, callback, { success: true, data: result });
};
}
function getDataWindow(dataWindowId) {
return getComponent("^" + (dataWindowId || "DataWindow") + "$") || getComponent("DataPanel$");
}
function transformSelectedFeaturesSet(allSelectedFeatures) {
var ret = [];
for (var mapServiceId in allSelectedFeatures) {
if (!hasOwnProperty.call(allSelectedFeatures, mapServiceId)) continue;
var featureClassInfo = allSelectedFeatures[mapServiceId];
/*jshint forin:false */
for (var featureClassId in featureClassInfo) {
if (!hasOwnProperty.call(featureClassInfo, featureClassId)) continue;
var featureInfos = featureClassInfo[featureClassId];
/*jshint forin:false */
for (var featureId in featureInfos) {
ret.push({
featureId: featureId,
featureClassId: featureClassId,
mapServiceId: mapServiceId
});
}
}
}
return ret;
}
/**
* @class Analysis
* Analysis representation. Instances of this type are not supposed to
* be created from the client code but appear in callbacks of {@link $GP.queries}
*/
function Analysis(config) {
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.config internal config
* @property {Object} _.portalAnalysis internal portal analysis representation
* @private
*/
this._ = {
config: config.config,
portalAnalysis: config.portalAnalysis
};
}
Analysis.prototype = {
/**
* Returns ID of the analysis
*/
get_id: function () {
return this._.portalAnalysis.get_id();
},
/**
* Returns Name of the analysis
*/
get_name: function() {
return this._.portalAnalysis.get_name();
},
/**
* Adds analysis result to the legend (and to map)
* @method addToLegend. Alias for addToMap
* @param {Object} [config] If not passed, then callback can be passed as first parameter
* @param {String} [config.parentLayerName] Name of the root layer where this analysis legend item should be added
* @param {String} [config.parentLayerId] ID of the root layer where this analysis legend item should be added
* @param {Function} callback
* @param {Function} errback
* @return {void}
*/
addToLegend: function(config, callback, errback) {
this.addToMap(config, callback, errback);
},
/**
* Adds analysis result to the legend (and to map)
* @method addToMap
* @param {Object} [config] If not passed, then callback can be passed as first parameter
* @param {String} [config.parentLayerName] Name of the root layer where this analysis legend item should be added
* @param {String} [config.parentLayerId] ID of the root layer where this analysis legend item should be added
* @param {Function} callback
* @param {Function} errback
* @return {void}
*/
addToMap: function (config, callback, errback) {
if (typeof config === T_FUNCTION) {
config = {};
callback = config;
errback = callback;
}
config = config || {};
var originalConfig = this._.config,
analysisId = this._.portalAnalysis.get_id(),
am = getPortalObj(P_ANALYSIS_MANAGER),
analysis = am.findAnalysis(analysisId),
parentLayerName = config.parentLayerName,
parentLayerId = config.parentLayerId,
mapState = getMapState(originalConfig),
mapServiceId = analysis.get_mapServiceId();
gp.legend.add({
ids: [analysisId],
parentLayerName: parentLayerName,
parentLayerId: parentLayerId,
mapServiceId: mapServiceId,
mapStateId: mapState.get_id(),
allowedTypes: [0,4]
}, callback, errback);
},
/**
* Removes analysis from legend
* @method removeFromLegend
* @param {Function} callback
*/
removeFromLegend: function (callback) {
var lit = gp.legend.find( { id: this._.portalAnalysis.get_id() } );
lit[0].remove(callback);
},
/**
* Permanently removes analysis (also from legend)
* @method remove
* @param {Function} callback
*/
remove: function (callback, errback) {
var dataWindow = getDataWindow();
if (dataWindow) {
dataWindow.closeDataWindow(this._.portalAnalysis.get_mapServiceId(), this._.portalAnalysis.get_id());
}
getPortalObj(P_ANALYSIS_MANAGER).deleteAnalysis(this._.portalAnalysis.get_id(), function (arg) {
if (!!arg.error) {
fire(F_FAILURE, errback, { success: false });
} else {
fire(F_SUCCESS, callback, { success: true });
}
});
},
/**
* Returns analysis feature collection
* @method getData
* @param {Object} [config] config can be skipped
* @param {Object} [config.force] whether to force data retrieval from the server
* @param {Function} callback
* @param {Function} [errback] callback executed if operation fails
* @return {Object} feature collection in geoJSON format
*/
getData: function (config, callback, errback) {
if (typeof config === T_FUNCTION) {
callback = config;
errback = callback;
config = {};
}
var force = config.force || false,
forceObj = {},
analysisId = this._.portalAnalysis.get_id(),
mapServiceId = this._.portalAnalysis.get_mapServiceId(),
service = getPortalObj(P_MAPSERVICE_MANAGER).findMapService( mapServiceId ),
lit = service.findLegendItemDefinition( analysisId ),
crsId = gp.crs.getCurrent(),
crs = getPortalObj(P_CRS),
quirks = getPortalObj(P_QUIRKS),
swapped = service.get_definition().name === "WFS" && crs.qualifiesForSwapById(crsId, "WFS");
if (swapped && quirks.incorrectlySwapsCoordinates(mapServiceId, crsId)) {
swapped = !swapped;
}
forceObj[analysisId] = force;
service.getFeatureDataset( [lit], makeDatasetCallback({
swapped: swapped
}, callback, errback), null, {force: forceObj});
},
/**
* Adds the query results to the data window
* @method addToDataView
* @param {Object} config
* @param {Function} callback
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
addToDataView: function (config, callback, errback) {
config = config || {};
var analysisId = this._.portalAnalysis.get_id(),
mapServiceId = this._.portalAnalysis.get_mapServiceId(),
service = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId),
lid = service.findLegendItemDefinition(analysisId),
dataWindow = getDataWindow(config.dataWindowId),
options = {}; // for future usage
if (!lid) {
return fire(F_FAILURE, errback, { success: false, message: "No LegendItemDefinition for analysisId=" + analysisId });
}
if (dataWindow) {
dataWindow.set_hidden(false);
dataWindow.selectAndAdd([lid], options);
return fire(F_SUCCESS, callback, { success: true, analysisId: analysisId, mapServiceId: mapServiceId });
} else {
return fire(F_FAILURE, errback, { success: false, message: "No DataWindow object" });
}
}
};
/**
* Event Listener. This class is not exposed in the API as instances of it
* are not supposed to appear in the client code. Purpose of this
* documentation is to provide documentation for instances of this class
* existing in the API:
*
* {@link $GP.events#mapMoved}
*
* {@link $GP.events#featureInfoRequested}
*
* @class EventListener
*/
function EventListener(config) {
this._eventName = config.eventName;
this._defaultHandlers = config.defaultHandlers || [];
this._f = {};
this._decorator = config.decorator;
}
EventListener.prototype = {
/**
* Register a handler to the event
* @method
* @param {Object/Function} config Configuration options or the handler function
* @param {Function} config.handler handler to be invoked on the event occurence
* @param {Boolean} [config.preventDefaults=true] Whether to suppress default event
* @param {Function} [config.predicate] config.handler is executed only if config.predicate
* function returns true. Predicate is invoked before the request is sent to the server.
* In combination with config.fallbackToDefaults it is possible to have custom
* feature info handler only for certain objects (or map areas)
* @param {Boolean} [config.fallbackToDefaults=true] Whether to execute default handler
* if config.predicate returns false, even if default handler is supressed
* @param {String} [config.token] Token for registration. It must be unique
* listeners (that means coming from Portal internals). If it is not provided, an unique
* token is generated.
* @return {String} token. Token for the event handler that should be used for
* unregistering it. If it is null, it means that registration was not successfull.
*/
register: function (config) {
if (typeof config === T_FUNCTION)
config = {handler: config};
var handler = config.handler,
scope = config.scope,
preventDefaults = typeof config.preventDefaults === T_UNDEFINED ? true : config.preventDefaults,
fallbackToDefaults = typeof config.fallbackToDefaults === T_UNDEFINED ? true : config.fallbackToDefaults,
token = config.token || createUuid(),
defaultHandlers = this._defaultHandlers;
if (this._f[token] || typeof handler !== T_FUNCTION)
return null;
if (this._decorator) {
if ([config.predicate, config.requestcompleted].some(function(x) {
return typeof x === T_FUNCTION;
}) && fallbackToDefaults) {
// Fallback to execution of default handlers identified by
// reference to the function and reference to the scope.
// This is a consequence of current $event implementation (13.0)
config.fallback = function(eventName, eventArgs, sender) {
for (var i = 0, l = defaultHandlers.length; i < l; i++) {
var h = defaultHandlers[i].get_handler(),
s = defaultHandlers[i].get_scope();
if (typeof h === T_FUNCTION)
h.call(s, eventName, eventArgs, sender);
}
};
}
handler = this._decorator(config);
}
var wrapper = function (eventName, eventArgs, sender) {
handler.call(scope, eventName, eventArgs, sender);
};
this._f[token] = wrapper;
if (preventDefaults)
this.suppressDefaults();
getPortalObj(P_EVENT).register(this._eventName, wrapper, null);
return token;
},
/**
* Unregister the event handler
* @method
* @param {Object/String} config Configuration options or the token
* @param {String} config.token. Token obtained while registering event handler
*/
unregister: function (config) {
var justtoken = typeof config === T_STRING,
token = justtoken ? config : config.token,
scope = justtoken ? null : config.scope;
if (this._f[token]) {
getPortalObj(P_EVENT).unregister(this._eventName, this._f[token], scope);
this.restoreDefaults();
delete this._f[token];
}
},
/**
* Suppress internal Portal handlers attached to the event
* @method
*/
suppressDefaults: function () {
for (var i = 0, l = this._defaultHandlers.length; i < l; i++) {
var handler = this._defaultHandlers[i].get_handler(),
scope = this._defaultHandlers[i].get_scope();
if (handler) // scope may be null or undefined
getPortalObj(P_EVENT).unregister(this._eventName, handler, scope);
}
this._defaultsSuppressed = true;
},
/**
* Restore internal Portal handlers attached to the event
* @method
*/
restoreDefaults: function () {
if (!this._defaultsSuppressed) return;
for (var i = 0, l = this._defaultHandlers.length; i < l; i++) {
var handler = this._defaultHandlers[i].get_handler(),
scope = this._defaultHandlers[i].get_scope();
if (handler) // scope may be null or undefined
getPortalObj(P_EVENT).register(this._eventName, handler, scope);
}
},
/**
* Suppress handlers attached to the event
* @param {Boolean} [suppressDefaults=false] whether to suppress also internal Portal events
* @method
*/
suppress: function(suppressDefaults) {
for (var wrapper in this._f) {
if (hasOwnProperty.call(this._f, wrapper))
getPortalObj(P_EVENT).unregister(this._eventName, this._f[wrapper], null);
}
if (suppressDefaults)
this.suppressDefaults();
},
/**
* Restore handlers attached to the event
* @param {Boolean} [restoreDefaults=false] whether to restore also internal Portal events
* @method
*/
restore: function(restoreDefaults) {
for (var wrapper in this._f) {
if (hasOwnProperty.call(this._f, wrapper))
getPortalObj(P_EVENT).register(this._eventName, this._f[wrapper], null);
}
if (restoreDefaults)
this.restoreDefaults();
}
};
var Queue = function(scope) {
scope = scope || {};
this.scope = scope;
this.funcs = [];
};
Queue.prototype.push = function(fn) {
this.funcs.push(fn);
};
Queue.prototype.flush = function() {
var fn;
/*jshint -W084 */
while (fn = this.funcs.shift())
fn.call(this.scope);
};
var RowActionPlugin = function (descriptors, actionsFilter, actionsFilterScope) {
this._descriptors = descriptors,
this._actionsFilter = actionsFilter;
this._actionsFilterScope = actionsFilterScope;
};
RowActionPlugin.prototype = {
getRowActionButtons: function(properties, scope) {
var actions = this._descriptors.map(function(d) {
if (typeof d.predicate === T_FUNCTION && !d.predicate.call(properties))
return [];
return {
text: d.text,
id: d.id,
style: d.style,
handler: function(e, rowInfo) {
var lid = properties.legendItemDefinition.get_sourceLegendItemDefinition() || properties.legendItemDefinition;
d.handler.call(d.scope || scope || gp.ui.dataView, {
featureId: rowInfo.id,
data: rowInfo.data,
mapServiceId: properties.mapServiceId,
featureClassId: lid.get_id(),
id: properties.legendItemDefinition.get_id(),
clipboard: properties.clipboard,
fromClipboard: properties.source.fromClipboard,
canDelete: properties.canDelete
}, e);
}
};
});
return actions.filter(this._actionsFilter, this._actionsFilterScope);
}
};
/* API */
// Global variable!
originalgp = window.$GP;
/**
* # Geospatial Portal JavaScript API #
*
* Keep making simple things simple and complex things possible with our new JavaScript API!
*
* ## Getting the API ##
* To use the API, appropriate script file must be included:
* <script type="text/javascript" src="js/API.min.js"></script>
* for the minified version or
* <script type="text/javascript" src="js/API.js"></script>
* for the debug version.
*
* All _aspx_ files in the SDK have the script file already included. _Example.aspx_ has it included
* in debug version and all the rest of the _*.aspx_ files have it included in minified version.
* **Don't forget to include it in your custom aspx!**
*
* ## Using the API ##
* SDK comes with broad set of live and editable examples and that should make it easy to start
* getting know what is where and what can be done. But how to start adding your custom code right
* into your project?""
*""
* It is very simple - just by adding new *script* tag in your *aspx*,""
* either pointing to a separate javascript file aimed for your customizations (as we all love order!):
* <script type="text/javascript" src="js/MyCustom.js"></script>
* or just a simple inline block for playing around:
* <script type="text/javascript" >
* // Example "0"
* $GP.ready(function () {
* // Display "Hello World!"
* $GP.ui.info("Hello World!");
*""
* // Add button to the toolbar (first tab)
* // that displays "Hello from toolbar!"
* $GP.ui.toolbar.add({
* xtype: "tbbutton",
* text: "?",
* handler: function (b) {
* $GP.ui.info("Hello from toolbar!");
* }
* });
* });
* </script>
* That script tag must be placed **after** the API.js (or API.min.js) script.
*
* **$GP.ready** function abstracts the process of loading the page and scripts necessary for
* Geospatial Portal to operate and, as its pretty descriptive name announces, executes your
* code when *$GP is ready*. So it may be generalized in that way:
* <script type="text/javascript" >
* $GP.ready(function () {
* // your code that interacts with the portal
* });
* </script>
* To give a detailed information, there are no contraindications to define functions outside
* of that block and then just make sure that they are executed inside of it.
*
* # Overview #
* API is written in functional style JavaScript and as you can observe in the examples
* most of the flow control happens in callbacks. This is not driven just by the aesthetics
* but mainly because of the need of consistency within the API as the Portal relies both on
* synchronous and asynchronous programming techniques.
*
* Most of the functions are written in that way:
* function foo (config, callback, errback) {
* //...
* }
* where config object simulates named parameters (so instead of passing 5 formal parameters
* we pass 1 but with a dictionary of properties), callback is function that is executed that
* is executed when the *foo* operation succeeds (so we can treat it as a kind of _return_ statememnt from
* the synchronous world) and errback is executed when the *foo* operation fails. Some methods do
* not have the _errback_ parameter and there the assumption is that _callback_ is executed all the time
* (this is for simplification where there is rather no possibility of "failing").
*
* # Scope #
* API covers:
* - map manipulation (zooming, panning, settings...)
* - legend manipulation (adding, removing layers)
* - registering services
* - redlining and drawing
* - pin layer
* - coordinate systems
* - ERDAS APOLLO connectivity
* - Name search
* - Basic user interface manipulation
* - User maps management
* - Selection set
* - Other Geospatial Portal features not mentioned here
*
* What this API does not cover is creation of specific GUI widgets, as specific GUI widgets should be prepared with
* specific GUI libraries aimed for creating specific GUI widgets! Current version of Geospatial Portal uses
* ExtJS version 2.2.1 and the simplest way of creating user interface would be just to follow the Portal and
* use embedded ExtJS library for GUI. GUI elements can be inserted into the sidebar and toolbar. Of course it
* is possible to reach totally customized GUI for Geospatial Portal with a little effort of sculpting both in *aspx*
* files and preparing controls in pure JavaScript!""
*
* ***
* Enjoy using Geospatial Portal JavaScript API!
* @class $GP
* @singleton
*/
gp = {
_queue: new Queue(window),
_handlePageLoad: function () {
if (this.__pageLoadRegistered) return;
// pageLoad is executed not only when everything is loaded but also with every ASP.NET postback
// we want to call it just once and then just execute fn
var oldonload = window.pageLoad;
window.pageLoad = function () {
if (typeof oldonload === T_FUNCTION)
oldonload();
if (gp.__pageLoaded)
return;
gp._queue.flush();
gp.__pageLoaded = true;
window.pageLoad = oldonload;
};
this.__pageLoadRegistered = true;
},
/**
* Wrapper for window.onload that preserves other window.onload if it is present.
* The load event fires at the end of the document loading process. At this point,
* all of the objects in the document are in the DOM, and all the images and sub-
* frames have finished loading.
* @param {Function} fn Function to be executed after document is ready
*/
ready: function (apiVersion, fn) {
if (typeof apiVersion === T_FUNCTION && !isSet(fn)) {
fn = apiVersion;
apiVersion = "v1.0";
warn("[DEPRECATED] you should call \"ready\" function using version as first parameter. Using default version: v1.0");
}
if (!apiVersion.match(/^v1\./i))
throw new Error("API in version " + apiVersion + " doesn't exist");
this._handlePageLoad();
this._queue.push(fn);
if (gp.__pageLoaded)
this._queue.flush();
},
/**
* Version
* @property version
*/
version: "16.8.0",
/**
* Allows to use $GP without conflicts
* For example: var foo = $GP.noConflict()
* @method noConflict
* @return {Object}
*/
noConflict: function () {
window.$GP = originalgp;
return this;
},
/**
* Settings for search
* @property searchDefaults
*/
searchDefaults: {
ep: "all",
action: "search",
handler: "SearchHandler",
mode: 1,
profile: "full",
classType: "com.erdas.rsp.babel.model.CatalogItem"
}
};
/**
* Unified search access point and a shortcut for specific searches
*
* Currently supported search types are:
* - name search (default) Search is performed on all INameSearcher services
* registered in the Portal unless another entry point is specified with
* "ep" parameter
* @deprecated 16.8.0
* - ERDAS APOLLO search ({searchType: "apollo"})
*
* Examples:
*
* Name search with WFSG:
*
* $GP.search({
* query: "Wars"
* },
* function (ret) {
* ret.results.forEach(function (item) {
* $GP.map.pin.add({
* featureClassId: "sample",
* x: item.point.x,
* y: item.point.y
* });
* });
* });
*
*
* @deprecated 16.8.0
* Search in ERDAS APOLLO catalogue:
*
* [Simple]
*
* $GP.search({
* searchType: "apollo",
* keywords: "Cherokee"
* }, function finish(response) {
* $GP.ui.info($GP.utils.serialize(response.results));
* });
*
* or
*
* $GP.search.apollo({
* keywords: "Cherokee",
* }, function finish(response) {
* $GP.ui.info($GP.utils.serialize(response.results));
* });
*
* [Advanced]
*
* $GP.search({
* searchType: "apollo",
* keywords: "world",
* profile: "full",
* classType: "com.erdas.rsp.babel.model.imagery.DatasetReference",
* addToMapEnabled: true,
* wmtsEnabled: true,
* ecwpEnabled: true,
* jpipEnabled: false,
* downladable: true,
* orderBy: "registrationDate desc",
* dateType: "1",
* startDate: "2008-06-19T00:00:00",
* endDate: "2013-06-25T23:59:59",
* cswQuery: null,
* start: 0,
* limit: 100,
* bbox: ["-30, 33 ,-30, 75, 80,75, 80,33"],
* geometryType: "POLYGON"
* }, function finish(response) {
* $GP.ui.info($GP.utils.serialize(response.results));
* });
*
* [Operations on ApolloSearchResult]
*
* $GP.search({
* searchType: "apollo",
* keywords: "Cherokee"
* }, function finish(response) {
* if (response.success) {
* response.results[1].addToMap({
* type: "wms"
* }, function (response) {
* if (response.success)
* response.item.zoom({});
* });
* }
* });
*
* Search type can be specified by using config.searchType parameter
* @class $GP.search
*
* @singleton
*/
gp.search = function(config, callback, errback) {
if (config.searchType === "apollo") {
gp.search.apollo(config, callback, errback);
} else {
gp.search.names(config, callback, errback);
}
};
function handleExecutor(callback, errback, fn, fnret) {
return function(executor) {
if (!getPortalObj(P_UTIL).checkExecutor(executor, getPortalObj(P_UTIL).showError)) {
fire(F_FAILURE, errback, { success: false });
return;
}
var obj = executor.get_object();
if (obj.error) {
if (obj.sessionExpired) {
getPortalObj(P_SESSION_MANAGER).resume();
}
fire(F_FAILURE, errback, { success: false, message: obj.error });
return;
}
var ret = {
success: true,
results: fn(obj)
};
if (typeof fnret === T_FUNCTION)
ret = fnret(ret);
fire(F_SUCCESS, callback, ret);
};
}
function getProcesses(callback, errback) {
return handleExecutor(callback, errback, function(o) {
return o.processes.map(function(item) {
return new Process({
portalApolloProcess: item
});
});
});
}
function getASR(callback, errback, pName, fnret) {
return handleExecutor(callback, errback, function(o) {
return o[pName].map(function(item) {
return new ApolloSearchResult({
portalSearchResult: item
});
});
}, fnret);
}
/**
* ERDAS APOLLO Catalogue search
* @method apollo
* @param {Object} config search configuration
* @param {Number} [config.start] start
* @param {Number} [config.limit] limit of results that will be fetched
* @param {String} [config.keywords]
* @param {String} [config.classType]
* @param {String} [confg.profile]
* @param {String} [config.bbox]
* @param {String} [config.geometryType]
* @param {Boolean} [config.addToMapEnabled]
* @param {Boolean} [config.wmtsEnabled]
* @param {Boolean} [config.ecwpEnabled]
* @param {Boolean} [config.jpipEnabled]
* @param {Boolean} [config.downladable]
* @param {String} [config.orderBy]
* @param {String} [config.geometryType]
* @param {String} [config.cswQuery]
* @param {String} [config.cswQuery]
* @param {String} [config.dateType]
* @param {String} [config.startDate]
* @param {String} [config.endDate]
* @param {String} [config.nameQualifier]
* @param {String} [config.template]
* @param {String} [config.crsId] Optional CRS identifier (for example EPSG:4326) if it needs to be other than current UI CRS
* @param {Boolean} [config.addToSearchPanel] Determines whether the result of the search be displayed in Search Panel.
* @param {String} [config.title] Used together with config.addToSearchPanel. Search Result Panel Tab title. If it is not passed, "Search" will be used
* @param {String} [config.addKeywordsToTitle] Used together with config.addToSearchPanel. Whether to append keywords to the tab title. Defaults to true.
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} [callback.result] Result object
* @param {Boolean} callback.result.success Always true
* @param {ApolloSearchResult[]} callback.result.results ERDAS APOLLO catalogue search result object representation
* @param {Function} [errback] callback executed if operation fails
* @param {Object} [errback.result] Result object
* @param {Boolean} errback.result.success Always false
* @param {String} errback.result.message Additional information
* @param {Object} errback.result Failure information object
* @return {void}
*/
gp.search.apollo = function(config, callback, errback) {
config = apply({}, config, gp.searchDefaults);
var apolloMapServiceId = getApolloMapServiceId();
if (!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var params = {
mapServiceId: apolloMapServiceId,
action: "search",
profile: config.profile,
keywords: config.keywords,
classType: config.classType,
bbox: config.bbox,
geometryType: config.geometryType,
orderBy: config.orderBy,
cswQuery: config.cswQuery,
addToMap: config.addToMapEnabled,
downloadable: config.downloadable,
wmts: config.wmtsEnabled,
ecwp: config.ecwpEnabled,
jpip: config.jpipEnabled,
dateType: config.dateType,
dateStart: config.startDate,
dateEnd: config.endDate,
currentCrs: config.crsId || gp.crs.getCurrent(),
nameQualifier: config.nameQualifier,
queryables: config.queryables ? gp.utils.serialize(config.queryables) : null,
template: config.template ? gp.utils.serialize(config.template) : null,
title: config.title,
addKeywordsToTitle: config.addKeywordsToTitle
};
if (isSet(config.limit) && isSet(config.start)) {
params.limit = config.limit;
params.start = config.start;
}
var apolloSearchRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: params,
callback: getASR(callback, errback, "results")
});
if(config.addToSearchPanel) {
gp.ui.searchResultPanel.displayResult(params);
}
else
apolloSearchRequest.invoke();
};
/**
* Search in name searcher services
* @method names
* @param {Object} config search configuration
* @param {String} [config.query] query
* @param {Number} [config.start] start
* @param {String} [config.ep] entry point (all, selective, parsing)
* @param {Number} [config.limit] limit of results that will be fetched
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} [callback.result] Result object
* @param {Boolean} callback.result.success Always true
* @param {NameSearchResult[]} callback.result.results Name searcher search results
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Failure information
* @param {Boolean} errback.result.success Always false
* @param {String} [errback.result.message] Additional information
* @return {void}
*/
gp.search.names = function(config, callback, errback) {
config = apply({}, config, gp.searchDefaults);
var nameSearchRequest = getPortalObj(P_WEB_REQUEST).create({
name: config.handler,
query: {},
body: {
action: config.action,
ep: config.ep,
query: config.query,
allMapServiceIds: Object.keys(getPortalObj(P_MAPSERVICE_MANAGER).get_mapServices())[0]
},
includeCRS: true,
callback: handleExecutor(callback, errback, function(o) {
return o.values.map(function(item) {
return new NameSearchResult({
portalSearchResult: item
});
});
})
});
nameSearchRequest.invoke();
};
window.$GP = gp;
/* jshint forin: false */
for (var pName in originalgp)
if (hasOwnProperty.call(originalgp, pName))
window.$GP[pName] = originalgp[pName];
// TODO: handle multiple map states
gp._getMapState = getMapState;
var hasJSON = typeof JSON === T_OBJECT;
/**
* Utilities
* @class $GP.utils
* @singleton
*/
gp.utils = {
/**
* Serializes object to JSON
* @method
* @param {Object} object
* @returns {String}
*/
serialize: hasJSON ? JSON.stringify : Sys.Serialization.JavaScriptSerializer.serialize,
/**
* Reads object from JSON
* @method
* @param {String} string
* @returns {Object}
*/
deserialize: hasJSON ? JSON.parse : Sys.Serialization.JavaScriptSerializer.deserialize,
/**
* Creates simple object with properties in form "xxx" instead of get_xxx() methods
* @method
* @param {Object} object
* @returns {Object}
*/
dump: dump,
sdump: function (o) { return this.serialize(this.dump(o)); },
/**
* Transform coordinates to screen coordinates
* @param {Object} config Point in the current CRS coordinates
* @param {Number} config.x X coordinate in the current CRS coordinates
* @param {Number} config.y Y coordinate in the current CRS coordinates
* @param {String} [config.mapStateId] map state id
* @return {Object} point with screen coordinates
* @return {Number} return.x X coordinate in the screen coordinates
* @return {Number} return.y Y coordinate in the screen coordinates
*/
getInScreenCrs: getXYinScreenResolution,
/**
* Transform coordinates to coordinates expressend in the current CRS
* @param {Object} config Point in the screen coordinates
* @param {Number} config.x X coordinate in the screen coordinates
* @param {Number} config.y Y coordinate in the screen coordinates
* @param {String} [config.mapStateId] map state id
* @return {Object} point with screen coordinates
* @return {Number} return.x X coordinate in the current CRS
* @return {Number} return.y Y coordinate in the current CRS
*/
getInMapCrs: getXYinMapResolution,
/**
* Creates new Globally Unique Identifier
* @param {String} [dataType] Data type of the identifier. Supported values are:
* 'System.Byte', 'System.Decimal', 'System.Double', 'System.Int16', 'System.Int32',
* 'System.Int64', 'System.Single', 'System.UInt16', 'System.UInt32', 'System.UInt64'
* @return {String} guid Globally Unique Identifier
*/
newGuid: function(dataType) {
return getPortalObj(P_SUPPORT).newGuid(dataType);
},
/**
* Converts array of {@link Feature} to GeoJSON FeatureCollection
* @param {Object} config
* @param {Feature[]} config.features
* @param {String} [config.featureClassId]
* @param {Function} callback Callback is executed on the resulting FeatureCollection
* @param {Function} errback Error callback
* @method toFeatureCollection
* @return {void}
*/
toFeatureCollection: function(config, callback, errback) {
findFeatures({
featureIds: config.features.map(function(proxy) { return proxy.get_id(); }),
featureClassId: config.featureClassId || config.features[0].get_featureClassId()
}, function(portalFeatures) {
var ret = {
"type": "FeatureCollection",
"features": portalFeatures.map(function(pf) {
return getPortalObj(P_GEOJSON).getFeature(pf);
})
};
fire(F_SUCCESS, callback, ret);
}, errback);
}
};
// TODO: mapStateId as constructor parameter
/**
* Managing services
* @class $GP.services
* @singleton
*/
gp.services = {
/**
* Registers and initializes the map service
* @method add
* @param {Object} config Configuration options
* @param {String} config.definitionName Definition Name of the service (WFS, WMS, ...)
* @param {String} config.url URL of the service
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always true
* @param {String} callback.result.msId Map Service ID
* @param {String} [callback.result.message] additional information
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always false
* @param {String} errback.result.msId ID of map service that failed to be registered
* @param {String} [errback.result.message] additional information
* @return {void}
*/
add: function (config, callback, errback) {
var that = this;
var definitionName = config.definitionName,
url = config.url;
// find whether map service with given URL is already registered
var msId;
if (definitionName === "MapPublisher") {
var mpms = findMapPublisherMapService(getPortalObj(P_MAPSERVICE_MANAGER), config);
msId = !mpms ? undefined : mpms.get_id();
} else {
msId = getPortalObj(P_MAPSERVICE_MANAGER).findMapServiceByUrl(url);
}
if (typeof msId !== T_UNDEFINED) {
fire(F_SUCCESS, callback, {
success: true,
msId: msId,
mapServiceId: msId,
message: "Map service already registered"
});
return this;
}
msId = config.id || gp.utils.newGuid();
var configInfo = config2dict(config);
function _mapServiceInitialized(eventName, eventArgs, sender) {
if (sender.get_id() !== msId) return;
if (eventName === E_MAPSERVICE_INITIALIZED) {
getPortalObj(P_LOG).writeInfo("map service initialized");
fire(F_SUCCESS, callback, {
success: true,
msId: msId,
mapServiceId: msId,
message: "Map service successfully registered"
});
}
if (eventName === E_MAPSERVICE_INIT_FAILED) {
getPortalObj(P_LOG).writeInfo("map service initialization failed");
fire(F_FAILURE, errback, {
success: false,
msId: msId,
mapServiceId: msId,
message: "Map service initialization failed"
});
}
// this.fire("afterinitialize");
getPortalObj(P_EVENT).unregister(E_MAPSERVICE_INITIALIZED, _mapServiceInitialized, that);
getPortalObj(P_EVENT).unregister(E_MAPSERVICE_INIT_FAILED, _mapServiceInitialized, that);
}
// this.fire("beforeregister");
getPortalObj(P_EVENT).register(E_MAPSERVICE_INITIALIZED, _mapServiceInitialized, this);
getPortalObj(P_EVENT).register(E_MAPSERVICE_INIT_FAILED, _mapServiceInitialized, this);
// register map service
getPortalObj(P_MAPSERVICE_MANAGER).registerMapService({
id: msId,
config: configInfo,
definition: {
name: definitionName
}
});
// this.fire("afterregister");
var ms = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(msId);
// this.fire("beforeinitialize")
ms.initialize();
return this;
},
/**
* Finds map service object
* @method find
* @param {Object} config Configuration options
* @param {String} [config.mapServiceId] Service Id (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.url] URL (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.name] Service Name (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.definitionName] Service Type (either mapServiceId or definitionName&name or url must be present)
* @param {Function} [config.predicate] predicate to test against services
* @param {Function} [callback] function that will be executed on results object
* @param {Object} callback.result list of map service objects
* @param {Boolean} callback.result.success Always success
* @param {MapService[]} callback.result.services list of map service objects
* @param {String[]} callback.result.ids list of IDs of map service objects
* @param {MapService} callback.result.service returned when there is just a single map service object
* @param {String} callback.result.mapServiceId returned when there is just a single map service object
* @param {Function} [errback] function executed on failure or when the result list is empty
* @param {Object} errback.result Failure information
* @param {Boolean} errback.result.success Always false
* @return {void/MapService[]} function returns list of services only if config.async is set
*/
// TODO: convenience predicates
find: function (config, callback, errback) {
var ret = [],
msm = getPortalObj(P_MAPSERVICE_MANAGER);
if (config.mapServiceId || config.url || config.definitionName) {
var ms =
msm.findMapService(config.mapServiceId) ||
(config.definitionName !== "MapPublisher" && msm.findMapService(msm.findMapServiceByUrl(config.url))) ||
(config.definitionName === "MapPublisher" && findMapPublisherMapService(msm, config)) ||
msm.findMapServicesByDefinitionName(config.definitionName);
if (Array.isArray(ms))
ms = ms.filter(function(item) {return item.get_name() === config.name})[0];
if (ms)
ret.push(new MapService({ portalMapService: ms }));
}
if (config.predicate) {
var services = getPortalObj(P_MAPSERVICE_MANAGER).get_mapServices() || {};
var servicesArray = Object.values(services);
for (var i = 0, l = servicesArray.length; i < l; i++) {
var service = new MapService({ portalMapService: servicesArray[i] });
if (config.predicate(service))
ret.push(service);
}
}
if (!config.successOnEmpty && ret.length < 1) {
fire(F_FAILURE, errback, {
success: false
});
} else {
fire(F_SUCCESS, callback, {
success: true,
services: ret,
ids: ret.map(function(s) { return s.get_id(); }),
service: ret.length === 1 ? ret[0] : undefined,
mapServiceId: ret.length === 1 ? ret[0].get_id() : undefined
});
}
if (config.async)
return this;
return ret;
}
};
/**
* Managing legend
* @class $GP.legend
* @singleton
*/
gp.legend = {
// display layers from the existing map service
_displayLayers: function (config, callback) {
var ids = findLegendItemDefinitions({
transformer: function (lid) { return lid.get_id(); },
names: config.names,
ids: config.ids,
mapServiceId: config.mapServiceId,
allowedTypes: config.allowedTypes
});
//TODO: WTF
var cp = getMapState(config).get_legend().get_flatLegend().length;
var priorities = {};
for (var j = ids.length - 1; j >= 0; j--)
priorities[ids[j]] = cp++;
var displayStyles = null;
if (typeof config.userStyleCallback === "function") {
//TODO: this array should have the same length as ids
displayStyles = [[{
userStyleCallback: function (internalFeature) {
var publicFeature = new Feature({
portalFeature: internalFeature,
mapStateId: getMapState(config).get_id()
});
var publicStyleConfig = config.userStyleCallback(publicFeature);
return getPortalStyle(publicStyleConfig);
}
}]];
}
var scope = this;
function onNewLegendItemAdded(eventName, eventArgs/*, sender*/) {
if (eventArgs.mapLayerConfigs && eventArgs.mapLayerConfigs.length !== 1)
return;
var mapLayerConfigId = eventArgs.mapLayerConfigs[0].get_id();
function onMapLayerRendered(eventName2, eventArgs2, sender2) {
if (mapLayerConfigId !== sender2.get_config().get_id())
return;
fire(F_SUCCESS, callback, { success: true, ids: ids });
getPortalObj(P_EVENT).unregister(E_MAPLAYER_RENDERED, onMapLayerRendered, scope);
}
getPortalObj(P_EVENT).unregister(E_LEGENDITEM_ADDED, onNewLegendItemAdded, scope);
getPortalObj(P_EVENT).register(E_MAPLAYER_RENDERED, onMapLayerRendered, scope);
}
if (config.successAfterRender)
getPortalObj(P_EVENT).register(E_LEGENDITEM_ADDED, onNewLegendItemAdded, scope);
// TODO: this method is totally wrong
getMapState(config).addLegendItemByDefinitionId([config.mapServiceId], [ids], config.name ? [config.name] : null, null, displayStyles, [priorities]);
if (!config.successAfterRender)
fire(F_SUCCESS, callback, { success: true, ids: ids });
},
_addToMapLayer: function (config, callback, errback) {
var mapServiceId = config.mapServiceId,
ids = config.ids,
names = config.names,
parentLayerName = config.parentLayerName,
parentLayerId = config.parentLayerId,
mapState = getMapState(config),
mapService = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId);
if (!mapState)
return fire(F_FAILURE, errback, { message: "Cannot find mapState" });
if (!mapService)
return fire(F_FAILURE, errback, { message: "Cannot find mapService" });
var msDef = mapService.get_definition(),
mapLayer = findMapLayer({
mapStateId: mapState.get_id(),
mapLayerName: parentLayerName,
mapLayerId: parentLayerId,
mapServiceId: mapServiceId
});
if (!mapLayer)
return fire(F_FAILURE, errback, { message: "Cannot find parent map layer" });
var mapLayerConfig = mapLayer.get_config(),
firstLegendItemPriority = mapLayerConfig.get_firstLegendItemPriority(),
isInLegend = !!mapState.get_legend().get_flatLegend()[firstLegendItemPriority];
if (msDef.onlyOneItemPerLayer && !isInLegend)
return fire(F_FAILURE, errback, { message: "Cannot add layer" });
var lids = findLegendItemDefinitions({
transformer: function (lid) { return lid; },
names: names,
ids: ids,
mapServiceId: mapServiceId,
allowedTypes: config.allowedTypes
});
if (!lids || !lids.length)
return fire(F_FAILURE, errback, { message: "No legend items found" });
mapState.addToMapLayer(lids, mapLayer);
fire(F_SUCCESS, callback, {
ids: ids,
mapStateId: mapState.get_id(),
parentMapLayerId: mapLayerConfig.get_id()
});
return void (0);
},
_addDynamic: function(config, callback, errback) {
var mapServiceId = config.mapServiceId,
mapService = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId),
definition = mapService.get_definition(),
definitionName = definition.name,
legendItemDefinitionIds = config.ids;
if (definitionName !== "WMS")
return fire(F_FAILURE, errback, { message: "Dynamic layers are supported only for WMS" });
var existingLegendItemDefinitions = mapService.get_legendItemDefinitions(),
lookup = {};
existingLegendItemDefinitions.forEach(function(lid) {
lookup[lid.get_id()] = true;
});
var dynamicLayerIds = legendItemDefinitionIds.filter(function(lidid) {
return !lookup[lidid];
});
mapService.createOuterLegendItemDefinitions(dynamicLayerIds, {
//TODO: handle parameters after supplementing core method
handler: function(/*actionId, args*/) {
return fire(F_SUCCESS, callback, { success: true, dynamicLayerIds: dynamicLayerIds });
}
});
return void(0);
},
/**
* Adds new legend items to the map
* @method add
* @param {Object} config Configuration options
* @param {String[]} [config.names] names
* @param {String[]} [config.ids] ids
* @param {String} [config.name] name of the grouping legend item
* @param {String} [config.url] URL
* @param {String} [config.definitionName] definition name
* @param {String} [config.mapServiceId] service id
* @param {Boolean} [config.topmostParent] Use the topmost layer of the service identified
* by config.mapServiceId as a parent for all added layers
* @param {String} [config.parentLayerName] Use parent layer of the service identified by
* config.mapServiceId and matching layer name
* @param {String} [config.parentLayerId] Use parent layer of the service identified by
* config.mapServiceId and matching layer id
* @param {Boolean} [config.dynamic] Supported only in WMS for now. Load layer that is not present
* in GetCapabilities document using non-standard GetLayer method. Works only with config.ids
* @param {Function} [config.predicate] predicate
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always true
* @param {String[]} callback.result.ids Legend Item IDs
* @param {Function} [errback] callback executed if operation fails
*/
// TODO: sane approach for APOLLO services with several endpoints
// TODO: handle layers not present in capabilities (APOLLO)
// TODO: handle situation when type of registered svc and provided in cfg do not match
// TODO: handle fancy options passed while registering map service
// TODO: handle uninitialized map service
// TODO: define criteria for success and failure
add: function (config, callback, errback) {
var mapServiceId = config.mapServiceId,
url = config.url,
definitionName = config.definitionName,
that = this;
// detect whether map service is already present
if (!mapServiceId && config.url) {
mapServiceId = getPortalObj(P_MAPSERVICE_MANAGER).findMapServiceByUrl(url);
}
var mapService = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId);
// if mapServiceId is not provided, add map service by URL
var dl = isAnySet(config.parentLayerId, config.parentLayerName, config.topmostParent) ? this._addToMapLayer : this._displayLayers;
if (config.dynamic) {
var origdl = dl;
dl = function(config2, callback2, errback2) {
that._addDynamic(config2, function() {
origdl(config2, callback2, errback2);
}, errback2);
};
}
if (mapService) {
config.mapServiceId = mapServiceId;
dl(config, callback);
} else if (url && definitionName) {
var serviceCfg = apply({}, config, {}, function (cfg, p) {
return typeof cfg[p] !== T_FUNCTION;
});
gp.services.add(serviceCfg, function (result) {
config.mapServiceId = result.msId;
dl(config, callback, errback);
});
} else fire(F_FAILURE, errback, { success: false });
},
/**
* Finds legend items
* @method find
* @param {Object} config If none of prediate, leaf, root, id and name properties are defined,
* then all legend items are returned
* @param {Boolean} [config.successOnEmpty] compatibility flag for returning success on empty result list
* @param {Boolean} [config.leaf] find only leafs in the legend item tree
* @param {Boolean} [config.root] find only top level items
* @param {String} [config.id] Find item with a given definition ID
* @param {String} [config.id] Find item with a given definition ID
* @param {Number} [config.legendItemId] numeric legend item ID
* @param {String} [config.name] Find item with a given display name
* @param {Function} [callback] Callback on success
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always true
* @param {LegendItem[]} callback.result.legendItems list of legend items
* @param {Function} [errback] Errback on failure
* @param {Object} errback.result Failure object
* @param {Boolean} errback.result.success Always false
* @return {void}
*/
// TODO: watch out for hierarchical services
find: function (config, callback, errback) {
if (typeof config === T_FUNCTION) {
callback = config;
config = {};
}
config = config || {};
var ret = [];
var predicate;
if (config.predicate)
predicate = config.predicate;
else if (config.leaf || config.root === false)
predicate = function (o) { return !!o._parent; };
else if (config.root || config.leaf === false)
predicate = function (o) { return !o._parent; };
else if (config.id)
predicate = function (o) { return o.get_definition().get_id() === config.id; };
else if (config.legendItemId)
predicate = function (o) { return o.get_id() === config.legendItemId; };
else if (config.name)
predicate = function (o) { return o.get_definition().get_name() === config.name; };
else
predicate = alwaysTrue;
var legend = getMapState(config).get_legend();
var items = rangerWalker({
childrenGetter: "get_legendItems",
items: legend.get_legendItems(),
predicate: predicate
});
for (var i = 0, l = items.length; i < l; i++)
ret.push(new LegendItem({ portalLegendItem: items[i], mapStateId: config.mapStateId }));
if (!config.successOnEmpty && ret.length < 1)
fire(F_FAILURE, errback, { success: false });
else
fire(F_SUCCESS, callback, { success: true, legendItems: ret });
}
};
/**
* User workspace
* @class Workspace
*/
function Workspace(portalWorkspace) {
this._ = {
portalWorkspace: portalWorkspace
};
this.id = portalWorkspace.id;
this.isPublic = portalWorkspace.isPublic;
this.name = portalWorkspace.name;
this.date = portalWorkspace.date;
this.maps = portalWorkspace.maps.map(function(m) { return new Map(m); });
}
Workspace.prototype = {
/**
* Displays the workspace
* @param {Object} [config] if config is not passed, the first param is callback
* @param {String} [config.mapStateId] mapStateId
* @param {Function} [callback] callback
* @param {Object} callback.result callback result
* @param {Workspace} callback.result.workspace callback result
* @returns {void}
*/
display: function(config, callback) {
if (typeof config === T_FUNCTION) {
callback = config;
config = {};
}
config = config || {};
var workspace = this,
mapStateId = getMapState(config).get_id();
gp.user._getMapStorageManager().displayWorkspace(this.id, mapStateId, function () {
fire(F_SUCCESS, callback, { workspace: workspace });
}, workspace);
},
/**
* Removes the workspace
* @method remove
*/
remove: function(callback) {
gp.user._getMapStorageManager().removeWorkspace(this.id, function() {
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: true });
});
},
/**
* Sets workspace's name
* @method set_name
*/
set_name: function(name, callback) {
gp.user._getMapStorageManager().setWorkspaceName(this.id, name, function() {
this.name = name;
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: true, map: this, workspace: this });
});
},
/**
* Sets workspace's isPublic flag
* @method set_isPublic
*/
set_isPublic: function(isPublic, callback) {
gp.user._getMapStorageManager().setWorkspacePublic(this.id, isPublic, function() {
this.isPublic = isPublic;
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: true, map: this, workspace: this });
});
}
};
/**
* User map
* @class Map
*/
function Map(portalMap) {
this._ = {
portalMap: portalMap
};
this.id = portalMap.id;
this.isPublic = portalMap.isPublic;
this.name = portalMap.name;
this.date = portalMap.date;
}
Map.prototype = {
/**
* Displays the map
* @param {Object} [config] if config is not passed, the first param is callback
* @param {String} [config.mapStateId] mapStateId
* @param {Function} [callback] callback
* @param {Object} callback.result callback result
* @param {Map} callback.result.map callback result
* @returns {void}
*/
display: function (config, callback) {
if (typeof config === T_FUNCTION) {
callback = config;
config = {};
}
config = config || {};
var map = this,
mapStateId = getMapState(config).get_id();
gp.user._getMapStorageManager().displayMap(this.id, mapStateId, function () {
fire(F_SUCCESS, callback, { map: map });
}, map);
},
/**
* Removes the map
* @method remove
*/
remove: function(callback) {
gp.user._getMapStorageManager().removeMap(this.id, function() {
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: true });
});
},
/**
* Sets map's name
* @method set_name
*/
set_name: function(name, callback) {
gp.user._getMapStorageManager().setMapName(this.id, name, function() {
this.name = name;
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: true, map: this });
});
},
/**
* Sets map's isPublic flag
* @method set_isPublic
*/
set_isPublic: function(isPublic, callback) {
gp.user._getMapStorageManager().setMapPublic(this.id, isPublic, function() {
this.isPublic = isPublic;
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: true, map: this });
});
}
};
/**
* Managing users - login, logout etc
* @class $GP.user
* @singleton
*/
gp.user = {
_getMapStorageManager: function () {
return getPortalObj(P_PLATFORM).MapStorage.MapStorageManager;
},
/**
* Attempts to log in user with given credentials
* @method login
* @param {Object} config Configuration options
* @param {String} config.username Username
* @param {String} config.password Username
* @param {Function} callback Callback executed after receiving server response
* @param {Object} callback.result Result object
* @param {String} callback.result.username Username
* @param {Object} callback.result.result Additional info
* @return {void}
*/
login: function (config, callback) {
manageUserProfile("logIn", config.username, config.password, callback);
},
/**
* Returns current user id (or null)
* @method getCurrent
* @return {String} current user id
*/
getCurrent: function () {
return getPortalObj(P_USER_MANAGER).getActiveUser();
},
/**
* Attempts to log in user with given credentials
* @method logout
* @param {Function} callback Callback executed after receiving server response
* @param {Object} callback.result Result object
* @param {String} callback.result.username Username
* @param {Object} callback.result.result Additional info
* @return {void}
*/
logout: function (callback) {
manageUserProfile("logOut", callback);
},
/**
* Managing user workspaces (anonymous by default)
* @class $GP.user.workspaces
* @singleton
*/
workspaces: {
/**
* Gets all user's workspaces. If predicate is not defined, all workspaces are returned
*
* $GP.user.workspaces.find(function (ret) {// ret.workspaces...});
*
*
* $GP.user.workspaces.find({
* predicate: function (ws) {return /^T/.test(ws.name)}},
* function (ret) {// ret.workspaces...});
*
* @method find
* @param {Function/Object} config search options
* @param {Function} [config.predicate] filter for searching workspaces function (workspace) {return {Boolean}}
* @param {Function} callback executed on workspace collection
* @param {Object} callback.result Result object
* @param {Workspace[]} callback.result.workspaces Workspaces list
* @return {void}
*/
find: function (config, callback) {
if (typeof config === T_FUNCTION) {
callback = config;
config = {};
}
config = config || {};
var findBy = makeFindByPredicate({ key: "id", value: config.id }, {key: "name", value: config.name});
var predicate = config.predicate || ((isSet(config.id) || isSet(config.name)) ? findBy : alwaysTrue);
gp.user._getMapStorageManager().getContent(function (result) {
var ret = [], ws = result.workspaces || [];
for (var i = 0, l = ws.length; i < l; i++) {
if (predicate(ws[i]))
ret.push(new Workspace(ws[i]));
}
fire(F_SUCCESS, callback, { workspaces: ret });
});
},
/**
* Adds workspace from current view
* @method add
* @param {Object} config Configuration
* @param {String} config.name Name of the workspace
* @param {String} [config.mapStateId] mapStateId ("map" by default)
* @param {Boolean} [config.isPublic] whether to make the workspace public
* @param {Boolean} [config.includeCredentials] whether to include services’ credentials
* @param {Boolean} [config.includeSelection] whether to include current selection
* @param {Function} callback Callback executed after creating a new workspace
* @param {Workspace} callback.workspace added workspace
* @return {void}
*/
add: function (config, callback) {
var saveWorkspaceConfig = apply({}, config, {
mapStateId: getMapState(config).get_id(),
handler: function(result) {
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
var workspace = new Workspace(result.workspace);
if (isSet(config.isPublic)) {
workspace.set_isPublic(config.isPublic, function(result) {
fire(F_SUCCESS, callback, result.workspace);
});
} else {
fire(F_SUCCESS, callback, workspace);
}
},
scope: this
});
gp.user._getMapStorageManager().saveWorkspace(saveWorkspaceConfig);
},
/**
* Removes workspaces with the given criteria
* @param {Object} config
* @param {String} [config.id]
* @param {String} [config.name]
* @param {Function} [config.predicate] filter for searching maps function (map) {return {Boolean}}
* @param {Function} config.callback success callback
* @param {Object} config.callback.ret returned object
* @param {String[]} config.callback.ret.ids IDs of the deleted workspaces
* @param {Function} config.errback failure callback
* @return {void}
*/
remove: function (config, callback, errback) {
this.find(config, function (ret) {
var toRemove = ret.workspaces;
if (!Array.isArray(toRemove) || toRemove.length === 0) {
fire(F_FAILURE, errback, { message: "No workspaces found" });
return;
}
var ids = toRemove.map(function(ws) { return ws.id; }),
nextCallback = function () {
var ws = toRemove.shift();
if (!ws) {
fire(F_SUCCESS, callback, { ids: ids });
} else {
ws.remove(nextCallback);
}
};
nextCallback();
}, errback);
},
/**
* Current workspace
* @class $GP.user.workspaces.current
* @singleton
*/
current: {
/**
* Clears current workspace
* @method clear
*/
clear: function(config, callback) {
config = config || {};
var m = getMapState(config);
m.clearMap();
getPortalObj(P_MAPSERVICE_MANAGER).removeAllMapServices(function() {
//var v = m.get_variants();
//m.removeAllMapStateVariants();
getPortalObj(P_EVENT).notify("mapStateVariantListChanged", { mapStateId: m.get_id() }, this);
fire(F_SUCCESS, callback, {success: true});
});
}
},
/**
* Updates workspace with given id and config params (config.id or config.name is requried)
* @param {Object} config
* @param {String} [config.id] - id of workspace to be updated
* @param {String} [config.name] - name of workspace to be updated or new name
* @param {Boolean} [config.isPublic] whether to make the workspace public
* @param {Boolean} [config.includeCredentials] whether to include services’ credentials
* @param {Boolean} [config.includeSelection] whether to include current selection
* @param {Function} callback Callback executed after creating a new workspace
* @param {Function} errback failure callback
* @return {void}
*/
update: function (config, callback, errback) {
if (!config.id & !config.name) {
fire(F_FAILURE, errback, { message: "No workspaceId or workspaceName provided" });
return;
}
this.find(config, function (ret) {
var toUpdate = ret.workspaces[0];
if (!toUpdate) {
fire(F_FAILURE, errback, { message: "No workspaces found with given workspaceID" });
return;
}
gp.user._getMapStorageManager().replaceWorkspace({
workspaceIdToReplace: toUpdate.id,
mapStateId: getMapState(config).get_id(),
name: config.name ? config.name : toUpdate.name,
handler: function (result) {
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
var workspace = new Workspace(result.workspace);
var isPublic = isSet(config.isPublic) ? config.isPublic : toUpdate.isPublic;
workspace.set_isPublic(isPublic, function (result) {
fire(F_SUCCESS, callback, result.workspace);
});
},
scope: this,
includeCredentials: true
});
}, errback);
}
},
/**
* Managing user maps (anonymous by default)
* @class $GP.user.maps
* @singleton
*/
maps: {
/**
* Gets all user's maps. If predicate is not defined, all maps are returned
*
* $GP.user.maps.find(function (ret) {// ret.workspaces...});
*
*
* $GP.user.maps.find({
* predicate: function (ws) {return /^T/.test(ws.name)}},
* function (ret) {// ret.maps...});
*
* @method find
* @param {Function/Object} config search options
* @param {Function} [config.predicate] filter for searching maps function (workspace) {return {Boolean}}
* @param {Function} callback executed on maps collection
* @param {Object} callback.result Result object
* @param {Map[]} callback.result.maps Maps list
* @return {void}
*/
find: function (config, callback) {
if (typeof config === T_FUNCTION) {
callback = config;
config = {};
}
config = config || {};
var findBy = makeFindByPredicate({ key: "id", value: config.id }, { key: "name", value: config.name });
var predicate = config.predicate || ((isSet(config.id) || isSet(config.name)) ? findBy : alwaysTrue);
gp.user._getMapStorageManager().getContent(function (result) {
var ret = [], ws = result.maps || [];
for (var i = 0, l = ws.length; i < l; i++) {
if (predicate(ws[i]))
ret.push(new Map(ws[i]));
}
fire(F_SUCCESS, callback, { maps: ret });
});
},
/**
* Adds map from current view
* @method add
* @param {Object} config Configuration
* @param {String} config.workspace id of the workspace
* @param {String} config.name Name of the workspace
* @param {String} config.isPublic whether to make the map public
* @param {Function} callback Callback executed after creating a new workspace
* @param {Map} callback.map added map
* @return {void}
*/
add: function (config, callback) {
// TODO: logic of saveMap is strange if it goes about choosing map name so we manually update the map name
gp.user._getMapStorageManager().saveMap("map", config.workspace || null, config.name, function (result) {
if (result && result.map && result.map.id)
gp.user._getMapStorageManager().setMapName(result.map.id, config.name);
var ret = new Map(result.map);
if (isSet(config.isPublic)) {
ret.set_isPublic(config.isPublic, function(ret2) {
fire(F_SUCCESS, callback, ret2.map);
});
} else {
fire(F_SUCCESS, callback, ret);
}
getPortalObj(P_EVENT).notify(E_USERMAPLIST_CHANGED, {}, this);
}, this);
},
/**
* Removes maps with the given criteria
* @param {Object} config
* @param {String} [config.id]
* @param {String} [config.name]
* @param {Function} [config.predicate] filter for searching maps function (map) {return {Boolean}}
* @param {Function} config.callback success callback
* @param {Object} config.callback.ret returned object
* @param {String[]} config.callback.ret.ids IDs of the deleted maps
* @param {Function} config.errback failure callback
* @return {void}
*/
remove: function (config, callback, errback) {
this.find(config, function (ret) {
var toRemove = ret.maps;
if (!Array.isArray(toRemove) || toRemove.length === 0) {
fire(F_FAILURE, errback, { message: "No maps found" });
return;
}
var ids = toRemove.map(function(ws) { return ws.id; }),
nextCallback = function () {
var ws = toRemove.shift();
if (!ws) {
fire(F_SUCCESS, callback, { ids: ids });
} else {
ws.remove(nextCallback);
}
};
nextCallback();
}, errback);
}
},
apollo: {
getCurrent: function() {
var service = getPortalObj(P_MAPSERVICE_MANAGER).findMapServicesByDefinitionName("Apollo")[0];
return service && service.get_userName();
}
}
};
/**
* Events container
* @class $GP.events
* @singleton
*/
gp.events = {
/**
* Fired when map range changes
* @event mapMoved
* @param {function(): void/Object} config function that is going to be executed when the event is fired or handler object
* @param {function(): void} [config.handler] Explicit version of passing the handler
* @param {String} [config.token] Option to pass token explicitly not to use a generated one
*/
mapMoved: new EventListener({
eventName: E_MAPRANGE_CHANGED
}),
/**
* Fired when new legend item is added
* @event legendItemAdded
* @param {function(): void/Object} config function that is going to be executed when the event is fired or handler object
* @param {function(legendItem: LegendItem): void} [config.handler] Explicit version of passing the handler
* @param {LegendItem} [config.handler.legendItem] LegendItem that has been added
* @param {String} [config.token] Option to pass token explicitly not to use a generated one
*/
legendItemAdded: new EventListener({
eventName: E_LEGENDITEM_ADDED,
decorator: function(config) {
var callback = config.handler;
return function (eventName, eventArgs/*, sender*/) {
eventArgs.mapLayerConfigs.forEach(function (mapLayerConfig) {
mapLayerConfig.forEachLegendItem(function (item) {
gp.legend.find({ legendItemId: item.get_id() }, function (ret) {
fireEventHandler("legendItemAdded", callback, ret.legendItems[0]);
});
}, this);
});
};
}
}),
/**
* Fired when legend item is removed
* @event legendItemRemoved
* @param {Function} handler function that is going to be executed when the event is fired
*/
legendItemRemoved: new EventListener({
eventName: E_LEGENDITEM_REMOVED,
decorator: function (config) {
var callback = config.handler;
return function (eventName, eventArgs/*, sender*/) {
var legendItemIds = eventArgs.removedLegendItemsDefinitionId;
legendItemIds.forEach(function (id) {
fireEventHandler("legendItemRemoved", callback, id);
});
};
}
}),
/**
* Fired when legend item (map layer) starts rendering
* @event legendItemLoadingStarted
* @param {Function} handler function that is going to be executed when the event is fired
*/
//TODO: find actual map layer
legendItemLoadingStarted: new EventListener({
eventName: E_MAPLAYER_RENDERING,
decorator: function (config) {
var callback = config.handler;
return function (eventName, eventArgs/*, sender*/) {
var mapLayerConfigId = eventArgs.mapLayerConfigId;
fireEventHandler("legendItemLoadingStarted", callback, { mapLayerConfigId: mapLayerConfigId });
};
}
}),
/**
* Fired when legend item (map layer) stops rendering
* @event legendItemLoadingFinished
* @param {Function} handler function that is going to be executed when the event is fired
*/
//TODO: find actual map layer
legendItemLoadingFinished: new EventListener({
eventName: E_MAPLAYER_RENDERED,
decorator: function (config) {
var callback = config.handler;
return function (eventName, eventArgs/*, sender*/) {
var mapLayerConfigId = eventArgs.mapLayerConfigId;
fireEventHandler("legendItemLoadingFinished", callback, { mapLayerConfigId: mapLayerConfigId });
};
}
}),
/**
* Fired when legend item visibility is modified
* @event legendItemVisibilityChanged
* @param {Function} handler function that is going to be executed when the event is fired
*/
legendItemVisibilityChanged: new EventListener({
eventName: E_LEGENDITEM_VISIBILITY_CHANGED,
decorator: function (config) {
var callback = config.handler;
return function (eventName, eventArgs/*, sender*/) {
var legendItemIds = eventArgs.legendItems.map(function (x) { return x.get_id(); });
legendItemIds.forEach(function (legendItemId) {
gp.legend.find({ legendItemId: legendItemId }, function (ret) {
fireEventHandler("legendItemVisibilityChanged", callback, ret.legendItems[0]);
});
});
};
}
}),
/**
* Fired when selection set of vector features is modified
* @event selectedFeaturesChanged
* @param {Function} handler function that is going to be executed when the event is fired
*/
selectedFeaturesChanged: new EventListener({
eventName: E_SELECTEDFEATURES_CHANGED,
decorator: function(config) {
var callback = config.handler;
return function (eventName, eventArgs /*, sender*/) {
var ret = {},
allSelectedFeatures = getPortalObj(P_SELECTEDFEATURES).getAllSelectedFeatures();
if (eventArgs && eventArgs.legendItemDefinitions) {
ret.featureClassIds = eventArgs.legendItemDefinitions
.map(function (lid) { return lid.get_id(); });
}
/*jshint forin:false */
ret.featureStubs = transformSelectedFeaturesSet(allSelectedFeatures);
fireEventHandler("selectedFeaturesChanged", callback, ret);
};
}
}),
/**
* Fired when feature info operation is invoked.
* It is an instance of the private type {@link EventListener}.
* @event featureInfoRequested
*
* 1) The simplest case in which we override default feature info behavior
* by providing a handler that is invoked after data is requested from
* the server:
* var handler = function (result) {
* // do something with feature info data from the server
* // for example:
* $GP.ui.info($GP.utils.serialize(result));
* }
* var token = $GP.events.featureInfoRequested.register(handler);
* // some sophisticated logic here ;-)
* // $GP.events.featureInfoRequested.unregister(token);
* By default, when the simple function is passed as the first parameter
* "preventDefaults" is set to true, so the handler overwrites the default
* featureInfo behavior. Please mind that for unregistering the handler you
* need the token that was obtained during handler registration
* Note that the code above may be written very expressively:
* var token = $GP.events.featureInfoRequested.register(function (result) {
* $GP.ui.info($GP.utils.serialize(result));
* });
* // some sophisticated logic here ;-)
* // $GP.events.featureInfoRequested.unregister(token);
*
* 2) It is possible to define more advanced logic of the custom featureInfo. In
* the following example custom featureInfo is executed only when geographic
* coordinates of the selected point indicate that it is not in Poland:
*
* var token = $GP.events.featureInfoRequested.register({
* handler: function (result) {
* $GP.ui.info($GP.utils.serialize(result))
* },
* predicate: function (args) {
* var point = {
* x: args.x,
* y: args.y
* },
* gpoint = $GP.utils.getInMapCrs(point);
* if (gpoint.x < 24 && 14 < gpoint.x && gpoint.y < 55 && 49 < gpoint.y) {
* return false
* }
* return true;
* }
* });
* // some sophisticated logic here ;-)
* // $GP.events.featureInfoRequested.unregister(token);
*
* 3) If default response from the server is not enough, it is possible
* to redirect request to a custom HTTP handler:
*
* var token = $GP.events.featureInfoRequested.register({
* handler: function (result) {
* $GP.ui.info($GP.utils.serialize(result))
* },
* httpHandler: "MyCustomFeatureInfoHandler.WebClient.ashx",
* transformargs: function (args) {
* args.foo = 42;
* return args;
* }
* });
* where MyCustomFeatureInfoHandler is name of the CLR type that
* is marked with WebClientHandlerAttribute.
*
* Parameters that can be passed to $GP.events.featureInfoRequested.register:
* @param {Object} config Configuration options
* @param {Function} config.handler callback function that takes
* feature info results obtained from the server as its first
* parameter. For example:
* function (result) {
* $GP.ui.info($GP.utils.serialize());
* }
*
* @param {Function} [config.transformargs] function that can modify
* object used in featureInfo retrieval
* @param {Function} [config.requestcompleted] function that can control
* display of featureInfo after retrieving the data. If this function returns
* false, then [config.fallback] is used for displaying the data. If it returns
* true, then callback is called. Callback can render custom feature info
* control. By default the [config.fallback] is bound to displaying standard
* featureInfo control.
* @param {Function} [config.predicate] Function that can prevent sending
* custom featureInfo request when it returns false. [config.fallback] is used
* in that condition. By default [config.fallback] executes standard featureInfo
* behavior.
* @param {Function} [config.fallback] Fallback for the callback. See
* [config.requestcompleted] and [config.predicate] for details.
* @param {Function} [config.httphandler] By default requests for
* feature info are send to "api/stateful/featureInfo" endpoint.
* It is possible to create your custom http handler marked with
* that attribute and pass its name here to provide custom handling.
*/
featureInfoRequested: new EventListener({
eventName: E_SHOW_FEATUREINFO,
decorator: createFeatureInfoHandler,
// Default handlers are defined by reference to the function and reference to the scope
// in case of the "showFeatureInfo" control, default handlers are _showFeatureInfo
// methods from default FeatureInfoControl and from FeatureInfoLightControl.
// Scope object is found by scanning objects that are instances of these controls.
defaultHandlers: [{
get_handler: function() {
return getPortalObj(GP_INTERNAL).featureInfoController._checkSessionBeforeFeatureInfo;
},
get_scope: function() {
return getPortalObj(GP_INTERNAL).featureInfoController;
}
}]
}),
/*
* Fired when feature info for all layers is invoked.
* Fires only when portal is in multi layer feature info mode
* ("displayInfoForAllLayers" should be turned on in web.config)
* It is an instance of the private type {@link EventListener}.
* @event featureInfoForAllLayersRequested
*/
featureInfoForAllLayersRequested: new EventListener({
eventName: E_SHOW_FEATUREINFO_ALL_LAYERS,
decorator: createFeatureInfoForAllLayersHandler,
// Default handlers are defined by reference to the function and reference to the scope
// in case of the "showFeatureInfo" control, default handlers are _showFeatureInfo
// methods from default FeatureInfoControl and from FeatureInfoLightControl.
// Scope object is found by scanning objects that are instances of these controls.
defaultHandlers: [{
get_handler: function() {
return getPortalObj(GP_INTERNAL).featureInfoController._checkSessionBeforeFeatureInfo;
},
get_scope: function() {
return getPortalObj(GP_INTERNAL).featureInfoController;
}
}]
})
};
/**
* Map control operations
* @class $GP.map
* @singleton
*/
gp.map = {
/**
* Gives id of current map variant
* @method getCurrentVariant
* @param {Object} [config]
* @param {String} [config.mapStateId] mapStateId ("map" by default)
* @param {Function} callback
* @param [String] callback.variantId ID of the current map variant
* @return {void}
*/
getCurrentVariant: function(config, callback) {
if (typeof config === T_FUNCTION && !isSet(callback))
callback = config;
if (!isSet(config) || typeof config !== T_OBJECT)
config = {};
var mapState = getMapState(config),
variantId = mapState.get_currentVariantId();
fire(F_SUCCESS, callback, { variantId: variantId });
},
/**
* Sets current map variant
* @method setCurrentVariant
* @param {Object} config
* @param {String} config.variantId ID of the desired map variant
* (map variant ids can be fetched using $GP.map.getVariantIds)
* @param {Boolean} [config.keepMapRange] Doesn't change the map range if true
* @param {String} [config.mapStateId] mapStateId ("map" by default)
* @param {Function} callback
* @param [String] callback.variantId ID of the current map variant
* @param [String] callback.mapStateId mapStateId
* @param {Function} errback Fired when arguments are invalid
* @return {void}
*/
setCurrentVariant: function (config, callback, errback) {
config = config || {};
var variantId = config.variantId,
setNewMapRange = !config.keepMapRange,
mapState = getMapState(config);
if (!isSet(variantId))
return fire(F_FAILURE, errback, { message: "No such a variant" });
function onVariantChanged(eventName, eventArgs) {
fire(F_SUCCESS, callback, { variantId: eventArgs.mapStateVariantId, mapStateId: eventArgs.mapStateId});
getPortalObj(P_EVENT).unregister(E_VARIANT_CHANGED, onVariantChanged, null);
}
getPortalObj(P_EVENT).register(E_VARIANT_CHANGED, onVariantChanged, null);
mapState.setVariant(variantId, true, setNewMapRange);
return void(0);
},
/**
* Gives ids of map variants of the current map
* @method getVariants
* @param {Object} [config]
* @param {String} [config.mapStateId] mapStateId ("map" by default)
* @param {Function} callback
* @param [String] callback.variantIds Variant IDs
* @return {void}
*/
getVariants: function (config, callback) {
if (typeof config === T_FUNCTION && !isSet(callback))
callback = config;
if (!isSet(config) || typeof config !== T_OBJECT)
config = {};
var mapState = getMapState(config),
variantIds = mapState.get_variants()
.filter(function (x) { return x.id !== "__default_"; })
.map(function (x) { return x.id; });
fire(F_SUCCESS, callback, { variantIds: variantIds });
},
/**
* Returns (in a callback) identifier of the current map mode ["WCMyvrMapControl","WCMapControl"]
* @method getMode
* @param {Object/Function} config. If config is a function, then it is treated as a callback
* @param {String} [config.mapStateId] Map State ID
* @param {Function} callback Callback
* @param {String} callback.id ID of the map control mode
* @return {void}
*/
getMode: function (config, callback) {
if (typeof config === T_FUNCTION && !isSet(callback))
callback = config;
var mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
definitionName = mapControl.get_definitionName();
return fire(F_SUCCESS, callback, { id: definitionName });
},
/**
* Change map mode (2d/3d)
* @method setMode
* @param {Object} config
* @param {String} config.id definition name the map control ["WCMyvrMapControl","WCMapControl"]
* @return
*/
setMode: function (config, callback, errback) {
config = config || {};
var mapState = getMapState(config),
mc = mapState.get_mapControl(),
dn = mc.get_definitionName(),
e = getPortalObj(P_EVENT);
function onMapControlChanged(eventName, eventArgs /*, sender*/) {
if (mapState.get_id() !== eventArgs.mapStateId) return;
fire(F_SUCCESS, callback, { success: true, id: config.id });
e.unregister("mapControlChanged", onMapControlChanged, null);
}
if (["WCMyvrMapControl", "WCMapControl"].indexOf(config.id) < 0) {
return fire(F_FAILURE, errback, { success: false });
}
if (config.id === dn)
return fire(F_SUCCESS, callback, { success: true });
e.register("mapControlChanged", onMapControlChanged, null);
e.notify("switch3d2d", { id: config.id });
return null;
},
/**
* Transform
* @method transform
* @param {Object} config Configuration parameters
* @param {Number} [config.offsetX]
* @param {Number} [config.offsetY]
* @param {Number} [config.x]
* @param {Number} [config.y]
* @param {Number} [config.minX]
* @param {Number} [config.minY]
* @param {Number} [config.maxX]
* @param {Number} [config.maxY]
* @param {Number} [config.scaleDenominator]
* @param {Number} [config.zoomFactor]
* @param {String} [config.mapStateId]
* @param {Function} callback Callback exectuted on success
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "True"
* @param {Function} errback Callback executed on failure
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always "False"
* @param {String} errback.result.message Message about errback
*/
transform: function(config, callback, errback) {
config = config || {};
var mapState = getMapState(config),
currentMapRange = mapState.get_mapRange().serialize(),
vec, point, bbox, scaleDenominator = config.scaleDenominator, options = config.options || {};
if (isAnySet(config.offsetX, config.offsetY)) {
vec = new (getPortalObj(P_SIZE))(isSet(config.offsetX) ? config.offsetX : 0, isSet(config.offsetY) ? config.offsetY : 0);
}
if (isEverySet(config.minX, config.minY, config.maxX, config.maxY)) {
bbox = new (getPortalObj(P_MAPRANGE))([config.minX, config.minY, config.maxX, config.maxY]);
}
if (isAnySet(config.x, config.y)) {
point = {
x: isSet(config.x) ? config.x : (currentMapRange[2] - currentMapRange[0])/2,
y: isSet(config.y) ? config.y : ((currentMapRange[3] - currentMapRange[1])/2)
};
}
if (!isAnySet(scaleDenominator, bbox, point, vec, config.zoomFactor)) {
fire(F_FAILURE, errback, { success: false, message: "Invalid parameters" });
return;
}
if (isSet(scaleDenominator)) {
var min = mapState.get_minScaleDenominator(),
max = mapState.get_maxScaleDenominator();
if (min)
scaleDenominator = Math.max(scaleDenominator, min);
if (max)
scaleDenominator = Math.min(scaleDenominator, max);
}
if (isSet(bbox))
mapState.set_mapRange(bbox, options);
if (isSet(point))
mapState.centerToPoint(point.x, point.y);
if (isSet(vec)) {
if (config.duration) {
var count = config.duration/100, stepX = vec.width/count, stepY = vec.height/count,
t = setInterval(function() {
if (--count) {
gp.map.transform({ offsetX: stepX, offsetY: stepY });
} else clearTimeout(t);
}, 100);
} else {
mapState.get_mapRange().movePx(vec, mapState.get_mapControl().get_mapSize());
getPortalObj(P_EVENT).notify(E_MAPRANGE_CHANGED, { mapStateId: mapState.get_id() }, this);
}
}
if (isSet(scaleDenominator)) {
mapState.get_mapControl().set_scaleDenominator(scaleDenominator, options);
getPortalObj(P_EVENT).notify(E_SCALE_CHANGED, { scaleDenominator: scaleDenominator, mapStateId: mapState.get_id() }, this);
}
if (isSet(config.zoomFactor))
mapState.zoom(config.zoomFactor);
fire(F_SUCCESS, callback, { success: true });
},
/**
* Pans the map
* @method pan
* @param {Object/Array} config
* @param {Number} [config.x] x
* @param {Number} [config.y] y
* @param {String} [config.mapStateId] map state id
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always "false".
* @param {String} errback.result.message Message what is wrong.
* See {@link $GP.map#transform}
*/
pan: function (config, callback, errback) {
var cfg = typeof config === T_OBJECT ? config : {};
if (Array.isArray(config)) {
cfg.offsetX = config[0];
cfg.offsetY = config[1];
}
else if (isEverySet(cfg.x, cfg.x)) {
delete config.x;
delete config.y;
cfg.offsetX = config.x;
cfg.offsetY = config.y;
}
this.transform(cfg, callback, errback);
},
/**
* Zooms the map according to the passed parameters. If no parameters
* are passed, then result of this method is setting map mode to "zoom
* by rectangle"
* @method zoom
* @param {Number[]/Number/Object} config bbox/zoomfactor/config
* @param {Number[]} [config.bbox] BBOX
* @param {Number} [config.zoomFactor] zoom factor
* @param {Number} [config.scaleDenominator] zoom scale denominator
* @param {String} [config.mapStateId] map state id
* @param {Number} [config.x] Point x
* @param {Number} [config.y] Point y
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always "false"
* @param {String} errback.result.message Message what is wrong.
* See {@link $GP.map#transform}
*/
zoom: function (config, callback, errback) {
var cfg = typeof config === T_OBJECT ? config : {};
if (typeof config === T_NUMBER) {
cfg.zoomFactor = config;
}
else if (Array.isArray(config)) {
cfg.minX = config[0];
cfg.minY = config[1];
cfg.maxX = config[2];
cfg.maxY = config[3];
} else if (isSet(config) && Array.isArray(config.bbox)) {
cfg.minX = config.bbox[0];
cfg.minY = config.bbox[1];
cfg.maxX = config.bbox[2];
cfg.maxY = config.bbox[3];
}
if (!isAnySet(cfg.zoomFactor, cfg.bbox, cfg.x, cfg.scaleDenominator)) {
var mapState = getMapState(config),
onMapRangeChanged = function(eventName, eventArgs) {
fire(F_SUCCESS, callback, { mapStateId: eventArgs.mapStateId });
getPortalObj(P_EVENT).unregister("mapRangeChanged", onMapRangeChanged, null);
};
getPortalObj(P_EVENT).register("mapRangeChanged", onMapRangeChanged, null);
getPortalObj(P_EVENT).notify("zoomRect", { mapStateId: mapState.get_id() }, null);
}
this.transform(cfg, callback, errback);
},
/**
* Returns information about the map
* @method info
* @param {Object} [config]
* @param {String} [config.mapStateId] map state id
* @param {Function} [callback] Callback function
* @param {Number[]} callback.bbox Bounding box of the map range in [minx, miny, maxx, maxy] order
* @param {Number} callback.center Center of the map
* @param {Number} callback.center.x Easting coordinate of the map central point expressed in current CRS
* @param {Number} callback.center.y Northing coordinate of the map central point expressed in current CRS
* @param {Number} callback.scaleDenominator Scale denominator
* @param {Number} callback.crs Current CRS
* @return {Object} return Returning object. Deprecated. Consider using callback instead.
* @return {Number[]} return.bbox Bounding box of the map range in [minx, miny, maxx, maxy] order
* @return {Number} return.center Center of the map
* @return {Number} return.center.x Easting coordinate of the map central point expressed in current CRS
* @return {Number} return.center.y Northing coordinate of the map central point expressed in current CRS
* @return {Number} return.scaleDenominator Scale denominator
* @return {Number} return.crs Current CRS
*/
//TODO: verify coordinates order
info: function (config, callback) {
config = config || {};
var mapState = getMapState(config),
mc = mapState.get_mapControl(),
mr = mapState.get_mapRange(),
bl = mr.getBottomLeft(),
ur = mr.getUpperRight(),
ce = mr.getCenter(),
bbox = [bl.x, bl.y, ur.x, ur.y];
var ret = {
bbox: bbox,
center: { x: ce.x, y: ce.y },
scaleDenominator: mc.get_scaleDenominator(),
crs: gp.crs.getCurrent()
};
fire(F_SUCCESS, callback, ret);
return ret;
},
/**
* Invokes feature info operation. It works in 3 basic modes:
*
* 1) Geographic point is provided through the user input
* $GP.map.featureInfo();
* If point (x and y or screenX and screenY properties)
* is not provided, then map control
* switches to feature info mode - mouse cursor is changed to cross
* and map control is waiting for the coordinates provided by the user
* click
*
* 2) Geographic point is provided programmatically
* $GP.map.featureInfo({x: 21, y: 51});
* $GP.map.featureInfo({screenX: 300, screenY: 400});
*
* 3) Feature ID is provided programmatically
* $GP.map.featureInfo({
* featureId: "USA2_STATES.22",
* legendItemDefinitionId: "{http://www.intergraph.com/geomedia/gml}USA2_STATES",
* url: "http://adt3/WFST_SP1/service.svc/get"
* });
*
* It is possible to provide a custom callback for the feature info operation
* invoked with this method - that custom callback may be invoked either
* parallelly to the default Portal's feature info handler:
*
* $GP.map.featureInfo({x: 0, y: 0}, function (result) {
* $GP.ui.info("Feature info request completed!");
* });
*
* or it can replace it (that happens when config.preventDefaults
* property is set):
*
* $GP.map.featureInfo({
* preventDefaults: true,
* }, function finish(response) {
* $GP.ui.info($GP.utils.serialize(response.results));
* });
*
* @method featureInfo
* @param {Object} [config] configuration properties
* @param {Number} [config.x] X value in the current CRS
* @param {Number} [config.y] Y value in the current CRS
* @param {Number} [config.screenX] X value in the screen coordinates
* (number pixels in the horizontal axis counted from the top left corner)
* @param {Number} [config.screenX] Y value in the screen coordinates
* (number pixels in the vertical axis counted from the top left corner)
* @param {String} [config.featureId] Feature ID - legendItemDefinitionId and
* either url of mapServiceId must be provided too to operate in that mode
* @param {String} [config.legendItemDefinitionId] Legend Item Definition ID - featureId and
* either url of mapServiceId must be provided too to operate in that mode
* @param {String} [config.mapServiceId] Map Service Id
* @param {String} [config.url] Map Service URL
* @param {Boolean} [config.queryAllLayers] indicates wether feature info should query all possible layers
* ("displayInfoForAllLayers" should be turned on in web.config)
* @param {Boolean} [config.queryAllVectorLayer] indicates if feature info should query all vector layers
* ("displayInfoForAllLayers" should be turned on in web.config)
* @param {Boolean} [config.preventDefaults] Prevent default feature info handler
* @param {String} [config.mapStateId] map state id
* @param {Function} [callback] callback invoked on feature info request. It
* is possible to locally overwrite default behavior of feature info control
* using callback and config.preventDefaults
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
//TODO: handle beforerequest, afterrequest, requestcompleted
//TODO: handle transformquery, transformargs
featureInfo: function(config, callback, errback) {
config = config || {};
var mapState = getMapState(config),
mc = mapState.get_mapControl(),
screenX = config.screenX,
screenY = config.screenY,
defaultCallback,
me = this,
ficscope = getPortalObj(GP_INTERNAL).featureInfoController;
// if screen coords are not passed, try to compute them using CRS coords
if ((typeof screenX === T_UNDEFINED || typeof screenY === T_UNDEFINED) &&
(typeof config.x !== T_UNDEFINED && typeof config.y !== T_UNDEFINED)) {
var c = getXYinScreenResolution(config);
screenX = c.x;
screenY = c.y;
}
// prevent invoking default feature info behavior
// WARNING: we are touching private portal guts here
if (config.preventDefaults) {
var handlers = getPortalObj(P_EVENT)._events.showFeatureInfo;
for (var i = 0, l = handlers.length; i < l; i++) {
if (handlers[i].scope && handlers[i].scope === ficscope) {
defaultCallback = handlers[i];
getPortalObj(P_EVENT).unregister(E_SHOW_FEATUREINFO, handlers[i].handler, handlers[i].scope);
break;
}
}
getPortalObj(GP_INTERNAL).featureInfoHelper.preventDefaults = true;
}
var handler = createFeatureInfoHandler({
handler: function(result) {
var failed;
try {
if (result.error)
failed = true;
else fire(F_SUCCESS, callback, {
success: true,
result: result
});
} catch (e) {
failed = true;
}
if (failed) {
fire(F_FAILURE, errback, {
success: false,
result: result
});
}
getPortalObj(P_EVENT).unregister(E_FEATUREINFO_ALL_FINISHED, handler, me);
getPortalObj(P_EVENT).unregister(E_SHOW_FEATUREINFO, handler, me);
if (defaultCallback)
getPortalObj(P_EVENT).register(E_SHOW_FEATUREINFO, defaultCallback.handler, defaultCallback.scope);
}
});
var multiLayersHandler = function(event, result) {
var failed;
try {
if (result.error)
failed = true;
else
fire(F_SUCCESS, callback, {
success: true,
result: result
});
} catch (e) {
failed = true;
}
if (failed) {
fire(F_FAILURE, errback, {
success: false,
result: result
});
}
getPortalObj(P_EVENT).unregister(E_FEATUREINFO_ALL_FINISHED, multiLayersHandler, me);
if (config.preventDefaults && defaultCallback)
getPortalObj(P_EVENT).register(E_SHOW_FEATUREINFO, defaultCallback.handler, defaultCallback.scope);
};
if (config.queryAllLayers || config.queryAllVectorLayers) {
getPortalObj(P_EVENT).register(E_FEATUREINFO_ALL_FINISHED, multiLayersHandler, me);
} else {
getPortalObj(P_EVENT).register(E_SHOW_FEATUREINFO, handler, me);
}
if (config.queryAllVectorLayers)
getPortalObj(GP_INTERNAL).featureInfoHelper.onlyVectorLayers = true;
if (config.featureId && config.legendItemDefinitionId && (config.mapServiceId || config.url)) {
var ms = gp.services.find({ url: config.url, mapServiceId: config.mapServiceId })[0],
lid = ms && ms._.ms.findLegendItemDefinition(config.legendItemDefinitionId);
if (!lid) {
fire(F_FAILURE, errback, {
success: false,
msg: "Legend item definition not found"
});
} else {
getPortalObj(P_EVENT).notify(E_SHOW_FEATUREINFO, {
x: screenX,
y: screenY,
featureId: config.featureId,
legendItemDefinition: lid
}, me);
}
} else if (typeof screenX !== T_UNDEFINED && typeof screenY !== T_UNDEFINED) {
// programatically invoke feature info with the given coordinates
if (config.queryAllLayers || config.queryAllVectorLayers) {
getPortalObj(GP_INTERNAL).featureInfoHelper.notifyFeatureInfoForAllLayers({
screenX: screenX,
screenY: screenY,
onlyVectorLayers: config.queryAllVectorLayers
});
} else {
mc.featureInfo({
x: screenX,
y: screenY
});
}
} else {
// set map control to waiting for the user click in feature info context
getPortalObj(P_EVENT).notify("featureInfo", {
mapStateId: mapState.get_id()
}, me);
}
},
/**
* Reloads all the layers
* @method refresh
* @param {Object} config
* @param {String} [config.mapStateId] map state id
* @param {Function} callback callback executed after all layers are rendered
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {Number} callback.result.numberOfRendered Shows the number of rendered layers.
* @param {Function} errback error callback
*/
refresh: function (config, callback, errback) {
var ev = getPortalObj(P_EVENT),
mapState = getMapState(config),
numOfLayers = mapState.get_mapControl().get_mapLayers().length,
numOfRendered = 0,
scope = this;
function count(eventName, eventArgs/*, sender*/) {
if (eventArgs.mapStateId !== mapState.get_id())
return;
numOfRendered++;
if (numOfRendered === numOfLayers) {
ev.unregister(E_MAPLAYER_RENDERED, count, scope);
fire(F_SUCCESS, callback, { success: true, numberOfRendered: numOfRendered });
}
}
try {
ev.register(E_MAPLAYER_RENDERED, count, scope);
mapState.get_mapControl()._resetLayers();
} catch (e) {
ev.unregister(E_MAPLAYER_RENDERED, count, scope);
fire(F_FAILURE, errback, { success: false });
}
}
};
/**
* Centers the map
* @param {Object} config
* @param {Number} config.x
* @param {Number} config.y
* @param {Function} callback
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {Function} errback
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always "false"
* @param {String} errback.result.message Message what is wrong.
* @method center
* See {@link $GP.map#transform}
*/
gp.map.center = gp.map.transform;
/**
* #Drawing, Redlining#
* $GP.map.draw is a container for specific drawing functions. But it
* is also a "shortcut" method that tries to guess how to dispatch the
* passed parameters and serves as a convenient general access point
* for drawing operations.
*
* Please note that Redlining and Drawing is supported only on the 2D
* map control.
*
* ##Point##
* Draw a point using GeoJSON API:
*
* $GP.map.draw({
* "type": "Point",
* "coordinates": [49, 69],
* "crsId": "EPSG:4326",
* "rotation": 0
* }, function (r) {
* console.log(r.feature.get_geoJSON());
* });
*
* Invoke drawing a point:
*
* $GP.map.draw({"type": "Point"},
* function (r) {
* console.log(r.feature.get_geoJSON());
* });
* or
*
* $GP.map.draw.point();
*
* ##MultiPolygon
* The following example draws multipolygon using
* the GeoJSON API and then centers the map:
*
* $GP.map.draw({
* "type": "MultiPolygon",
* "coordinates": [
* [
* [
* [102.0, 2.0],
* [103.0, 2.0],
* [103.0, 3.0],
* [102.0, 3.0],
* [102.0, 2.0]
* ]
* ],
* [
* [
* [100.0, 0.0],
* [101.0, 0.0],
* [101.0, 1.0],
* [100.0, 1.0],
* [100.0, 0.0]
* ],
* [
* [100.2, 0.2],
* [100.8, 0.2],
* [100.8, 0.8],
* [100.2, 0.8],
* [100.2, 0.2]
* ]
* ]
* ]
* },
* function (result) {
* $GP.map.zoom({
* x: 102,
* y: 2
* });
* console.log(result);
* console.log($GP.utils.serialize(result.features[0].get_geoJSON()))
* });
*
* ##GeometryCollection
*
* $GP.map.draw({
* "type": "GeometryCollection",
* "geometries": [{
* "type": "Point",
* "coordinates": [100.0, 0.0]
* }, {
* "type": "LineString",
* "coordinates": [
* [101.0, 0.0],
* [102.0, 1.0]
* ]
* }]
* },
* function (result) {
* $GP.map.zoom({
* x: 102,
* y: 2
* });
* console.log(result);
* console.log($GP.utils.serialize(result.features[0].get_geoJSON()))
* });
*
* ##Style
* You can use one of the styles provided by portal:
*
* $GP.map.draw({
* "type": "Feature",
* "geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [100.0, 0.0],
* [101.0, 0.0],
* [101.0, 1.0],
* [100.0, 1.0],
* [100.0, 0.0]
* ]
* ]
* },
* style: "highlight"
* })
*
* or try to roll your own, for example by copying it from the GPW:
*
* $GP.map.draw({
* "type": "Feature",
* "geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [100.0, 0.0],
* [101.0, 0.0],
* [101.0, 1.0],
* [100.0, 1.0],
* [100.0, 0.0]
* ]
* ]
* },
* style: {
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Compound style",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 0,
* "styles": [{
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Area styles",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 4,
* "styles": [{
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Area style",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 0,
* "styles": [{
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Boundary styles",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 2,
* "styles": [{
* "bold": false,
* "color": "#FF9900",
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Simple line style",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 0,
* "styles": [],
* "subtype": 0,
* "translucency": 0.4,
* "type": 2,
* "underline": false,
* "verticalAlignment": 0,
* "visible": true,
* "width": 4
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 0,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }, {
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Fill styles",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 3,
* "styles": [{
* "bold": false,
* "color": "#99CC00",
* "horizontalAlignment": 0,
* "imageHeight": 1,
* "imageUrl": null,
* "imageWidth": 1,
* "italic": false,
* "name": "Simple fill style",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 0,
* "styles": [],
* "subtype": 0,
* "translucency": 0.33,
* "type": 3,
* "underline": false,
* "verticalAlignment": 0,
* "visible": true,
* "width": 0
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 0,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 4,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 0,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }, {
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Linear styles",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 2,
* "styles": [{
* "bold": false,
* "color": "#000000",
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Simple line style",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 0,
* "styles": [],
* "subtype": 0,
* "translucency": 0,
* "type": 2,
* "underline": false,
* "verticalAlignment": 0,
* "visible": true,
* "width": 1
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 0,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }, {
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Point styles",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 1,
* "styles": [{
* "bold": false,
* "color": "#000000",
* "horizontalAlignment": 0,
* "imageHeight": 1,
* "imageUrl": null,
* "imageWidth": 1,
* "italic": false,
* "name": "Simple point style",
* "rotation": 0,
* "rule": null,
* "size": 3,
* "styleCollectionType": 0,
* "styles": [],
* "subtype": 0,
* "translucency": 0,
* "type": 1,
* "underline": false,
* "verticalAlignment": 0,
* "visible": true,
* "width": 0
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 0,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }, {
* "bold": false,
* "color": null,
* "horizontalAlignment": 0,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Text styles",
* "rotation": 0,
* "rule": null,
* "size": 0,
* "styleCollectionType": 6,
* "styles": [{
* "bold": false,
* "color": "#000000",
* "horizontalAlignment": 1,
* "imageHeight": 0,
* "imageUrl": null,
* "imageWidth": 0,
* "italic": false,
* "name": "Text style",
* "rotation": 0,
* "rule": null,
* "size": 10,
* "styleCollectionType": 0,
* "styles": [],
* "subtype": 0,
* "translucency": 0,
* "type": 6,
* "underline": false,
* "verticalAlignment": 1,
* "visible": true,
* "width": 0
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 0,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }],
* "subtype": 0,
* "translucency": 0,
* "type": 5,
* "underline": false,
* "verticalAlignment": 0,
* "visible": false,
* "width": 0
* }
* })
*
* You can also override one of existing default styles for geometry.
* For example point's `SimplePointStyle`:
*
* $GP.map.draw({
* "type": "Point",
* "styleType": "Intergraph.WebSolutions.Core.WebClient.Platform.Style.SimplePointStyle",
* "style": {
* "color": "hsla(155, 100%, 50%, .7)",
* "size": 16
* }
* });
*
*
* ##Circles##
* $GP.map.draw.circle();
*
* Please mind that circles are not included in GeoJSON specification and
* feature.get_geoJSON() for that particular case is not a standard GeoJSON.
* ##Arcs##
* $GP.map.draw.arc();
*
* Please mind that circles are not included in GeoJSON specification and
* feature.get_geoJSON() for that particular case is not a standard GeoJSON.
* ##Rectangles##
* $GP.map.draw.rectangle();
*
* ##Hacking##
* Disk. The following example takes advantage of the custom extension that
* Geospatial Portal uses with GeoJSON that enables handling circular
* and arc geometries. It draws a disk with the center in point [19, 51]
* and its bounding circle touching point [14, 53]. Fourth coordinate is
* used to mark whether point is a starting point of an arc and fifth is
* used to mark whether point is center of the circle/disk. Please mind that it is not
* standard GeoJSON!
*
* $GP.map.draw({
* "type": "Feature",
* "geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [19, 51, 0, 0, 1],
* [14, 53],
* [19, 51, 0, 0, 1]
* ]
* ],
* "crsId": "EPSG:4326"
* },
* "properties": {}
* });
*
* Please take care that in this particular case the way that circular
* geometries are handled in GeoJSON may change in the future, especially
* if this topic is taken into the scope of GeoJSON specification. Now it
* is out of the scope.
*
* @class $GP.map.draw
* @singleton
*/
// TODO: Decide on default layer name
// TODO: think about method queue chaining because of user input mode
gp.map.draw = function(config, callback, errback) {
if (!config.coordinates && config.type === "Point")
return gp.map.draw.point.call(gp.map.draw, config, callback, errback);
else if (!config.coordinates && config.type === "LineString")
return gp.map.draw.path.call(gp.map.draw, config, callback, errback);
else if (!config.coordinates && config.type === "Polygon")
return gp.map.draw.polygon.call(gp.map.draw, config, callback, errback);
else if (config.type)
return gp.map.draw.geojson.call(gp.map.draw, config, callback, errback);
return this;
};
gp.map.draw._perform = function (action, config) {
/**
* Default layer name and suffixes
* @property {String} defaultLayerName
* @property {Array} layerNameSuffixes
*/
this.defaultLayerName = "DefaultName";
this.layerNameSuffixes = ["_hideMeasure", "_showMeasure"];
var r = getPortalObj(P_REDLINING),
mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
showMeasure = config.showMeasure || false,
layerName = (config.layerName || this.defaultLayerName) + this.layerNameSuffixes[showMeasure ? 1 : 0],
layer = r.getNamedRedliningLayer(layerName, mapControl, showMeasure);
action.call(this, r, layer);
};
gp.map.draw._afterPerform = function(config, callback) {
return function(result) {
var portalFeature = result,
portalFeatures,
ret = { success: true },
isArray = Array.isArray(portalFeature);
portalFeatures = !isArray ? [portalFeature] : portalFeature;
ret.features = portalFeatures.map(function(pf) {
return new Feature({
portalFeature: pf,
mapStateId: config.mapStateId
});
});
if (!isArray)
ret.feature = ret.features[0];
fire(F_SUCCESS, callback, ret);
};
};
/**
* Draws GeoJSON objects on the map. Handles geometry types, Feature and FeatureCollection.
* See [GeoJSON](http://www.geojson.org/geojson-spec.html) for information concerning GeoJSON
* @method geojson
* @param {Object} config GeoJSON object to be drawn
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Array} callback.array
* @param {Function} [errback] callback executed if operation fails
*/
gp.map.draw.geojson = function(config, callback, errback) {
config = config || {};
config.defaultStyleName = config.defaultStyleName || "redlining";
var style = getPortalStyle(config);
if (config.type === "FeatureCollection") {
this._perform(function(redlining, layer) {
redlining.drawFeatureClassStub(layer, this._afterPerform(config, callback, errback), this, style, getPortalObj(P_GEOJSON).read(config));
}, config, callback, errback);
}
else {
this._perform(function(redlining, layer) {
redlining.drawFeatureStub(layer, this._afterPerform(config, callback, errback), this, style, getPortalObj(P_GEOJSON).readFeatureStub(config));
}, config, callback, errback);
}
return this;
};
/**
* Draw points. If coordinates (x && y || points) are not provided, method enters redlining mode
* @method point
* @param {Object} config Configuration options
* @param {Number} [config.x] X coordinate
* @param {Number} [config.y] Y coordinate
* @param {Array} [config.points] Points
* @param {String} [config.layerName] name of the layer
* @param {Number} [config.showMeasure] show measure
* @param {String} [config.mapStateId] map state id
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {Array} callback.result.features Array containing created features.
* @param {Feature} callback.result.feature
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Result object
*/
// TODO: validate parameters
// TODO: clarify callbacks
// TODO: swapped coordinates
gp.map.draw.point = function(config, callback, errback) {
config = config || {};
config.defaultStyleName = config.defaultStyleName || "redlining";
var point = (config.x && config.y) ? { x: config.x, y: config.y } : undefined,
points = config.points || [point],
style = getPortalStyle(config),
operator = function(pointe) {
return function (redlining, layer) {
redlining.drawPoint(layer, this._afterPerform(config, callback, errback), this, style, pointe);
};
};
for (var i = 0, l = points.length; i < l; i++)
this._perform(operator(points[i]), config, callback, errback);
return this;
};
/**
* Draws a line chain. If coordinates are not provided, method enters redlining mode
* @method path
* @param {Object} config Configuration options
* @param {Array} [config.points] Points
* @param {String} [config.layerName] name of the layer
* @param {Number} [config.showMeasure] show measure
* @param {String} [config.mapStateId] map state id
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
*/
gp.map.draw.path = function(config, callback, errback) {
config = config || {};
config.defaultStyleName = config.defaultStyleName || "redlining";
var t = this,
style = getPortalStyle(config);
t._perform(function(redlining, layer) {
redlining.drawPolyline(layer, t._afterPerform(config, callback, errback), t, style, config.points, config.geometryType);
}, config, callback, errback);
return t;
};
/**
* Draw a polygon. If coordinates (points) are not provided, method enters redlining mode
* @method polygon
* @param {Object} config Configuration options
* @param {Array} [config.points] Points
* @param {String} [config.layerName] name of the layer
* @param {Number} [config.showMeasure] show measure
* @param {String} [config.mapStateId] map state id
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
*/
// TODO: handle bounding geometry
gp.map.draw.polygon = function(config, callback, errback) {
config = config || {};
config.defaultStyleName = config.defaultStyleName || "redlining";
var bounds,
style = getPortalStyle(config);
this._perform(function(redlining, layer) {
redlining.drawPolygon(layer, this._afterPerform(config, callback, errback), this, style, config.points, config.geometryType, bounds);
}, config, callback, errback);
return this;
};
/**
* Draw rectangle
* @method rectangle
* @param {Object} config Configuration options
* @param {Array} [config.points] Points
* @param {String} [config.layerName] name of the layer
* @param {Number} [config.showMeasure] show measure
* @param {String} [config.mapStateId] map state id
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
*/
gp.map.draw.rectangle = function(config, callback, errback) {
return this.polygon(apply({}, config, {geometryType: 2}), callback, errback);
};
/**
* Draw circle
* @method circle
* @param {Object} config Configuration options
* @param {Array} [config.points] Points
* @param {String} [config.layerName] name of the layer
* @param {Number} [config.showMeasure] show measure
* @param {String} [config.mapStateId] map state id
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
*/
gp.map.draw.circle = function(config, callback, errback) {
return this.polygon(apply({}, config, {geometryType: 10}), callback, errback);
};
/**
* Draw arc
* @method arc
* @param {Object} config Configuration options
* @param {Array} [config.points] Points
* @param {String} [config.layerName] name of the layer
* @param {Number} [config.showMeasure] show measure
* @param {String} [config.mapStateId] map state id
* @param {Object/String} [config.style] style name or stub
* @param {Object/String} [config.defaultStyleName] style name
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
*/
gp.map.draw.arc = function (config, callback, errback) {
return this.polygon(apply({}, config, { geometryType: 9 }), callback, errback);
};
/**
* Clears drawing layer
* @method clear
*/
gp.map.draw.clear = function(config, callback) {
config = config || {};
if (!this.layerNameSuffixes) {
fire(F_FAILURE, callback, { success: false });
return;
}
for (var i = 0; i < this.layerNameSuffixes.length; i++) {
var r = getPortalObj(P_REDLINING),
mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
layerName = (config.layerName || this.defaultLayerName) + this.layerNameSuffixes[i],
showMeasure = config.showMeasure || false,
layer = r.getNamedRedliningLayer(layerName, mapControl, showMeasure);
r.eraseFeatures(layer);
}
fire(F_SUCCESS, callback, { success: true });
};
/**
* Sets visibility of the drawing layer
* @method setVisibility
* @param {Object} config
* @param {Boolean} config.visibility Visiblity of the redlining layer
* @param {Function} callback
*/
gp.map.draw.setVisibility = function(config, callback) {
config = config || {};
if (!this.layerNameSuffixes) {
fire(F_FAILURE, callback, { success: false });
return;
}
for (var i = 0; i < this.layerNameSuffixes.length; i++) {
var r = getPortalObj(P_REDLINING),
mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
layerName = (config.layerName || this.defaultLayerName) + this.layerNameSuffixes[i],
showMeasure = config.showMeasure || false,
layer = r.getNamedRedliningLayer(layerName, mapControl, showMeasure);
layer.set_visibility(!!config.visibility);
}
fire(F_SUCCESS, callback, { success: true });
};
function getPoint(g) {
var b = g.get_bounds(),
x = b._bottomLeft_X + (b._upperRight_X - b._bottomLeft_X) / 2,
y = b._bottomLeft_Y + (b._upperRight_Y - b._bottomLeft_Y) / 2;
return new (getPortalObj(P_POINT))(x, y);
}
/**
* Pin wrapper. Objects of this class shouldn't be manually crated
* @param {Object} config Configuration
* @param {Object} config.pinLayer pin layer reference
* @param {Object} config.featureClass feature class
* @param {Object} config.geojson GeoJSON
* @param {String} config.mapStateId mapStateId
* @class Pin
*/
function Pin(config) {
this._ = {
c: config
};
var fc = config.featureClass,
gj = config.geojson,
fs = getPortalObj(P_GEOJSON).readFeatureStub(gj),
actions = config.actions || [],
g = fs.attributes.filter(function(a) { return a.Key === fs.geometryFieldsNames[0]; })[0];
if (isSet(g) && !getPortalObj(P_POINT).isInstanceOfType(g.Value)) {
g.Value = getPoint(g.Value);
}
if (!isSet(g)) {
return;
}
this._.portalFeature = fc.importFeature(fs);
// TODO: this is because of MyvrMapControl dependendy...
this._.portalFeature.jsonRef = gj.properties || {};
this.over = new EventListener({ eventName: "pinOver" });
this.out = new EventListener({ eventName: "pinOut" });
this.clicked = new EventListener({ eventName: "pinClicked" });
/* jshint forin: false */
for (var actionName in actions) {
if (!hasOwnProperty.call(actions, actionName)) continue;
this[actionName].register(this._getHandler(actionName));
}
}
Pin.prototype = {
_getHandler: function (actionName) {
var t = this,
c = t._.c;
return function (eventName, eventArgs) {
if (!eventArgs || eventArgs.feature !== t._.portalFeature)
return;
c.actions[actionName].call(t, eventArgs.DOMElement);
};
},
/**
* Removes this pin from the map and its feature from the feature class
* @method remove
* @return void
*/
remove: function () {
this.over.suppress();
this.out.suppress();
this.clicked.suppress();
var t = this,
c = t._.c,
fcid = c.featureClass.get_id(),
features = c.pinLayer._featureClassCollection[fcid].get_features() || {};
for (var fid in features)
if (hasOwnProperty.call(features, fid) && features[fid] === t._.portalFeature) {
delete features[fid];
break;
}
c.pinLayer.refresh(fcid);
},
/**
* Centers the map on the object
* @method zoom
* @param {Object} config
* @param {Function} callback
* @return void
*/
zoom: function(config, callback) {
var g = this._.portalFeature.get_geometry(),
cfg = apply({}, config, {x: g.x, y: g.y});
gp.map.zoom(cfg, callback);
},
/**
* Returns Feature object
* @method getFeature
* @return {@link Feature} feature object
*/
getFeature: function() {
return new Feature({ portalFeature: this._.portalFeature });
}
};
/**
* #Pins
* Pins can be added to the map by passing GeoJSON data including FeatureCollections,
* Features and all geometry types. When geometry is not a point, then point is taken
* from the crossing of diagonals of the bounding geometry.
*
* Pins can have custom actions on mouseover ("over"), mouseout ("out") and mouseclick
* ("clicked") events. Actions may be common for all feature collection or different
* for every pin. These actions are expected to be functions that take HTML element
* as their parameter and have "this" bound to the Pin object, for example:
*
* function (element) {
* element.className = "wc_map_layer_red_pin";
* alert(this.getFeature().get_geoJSON().properties.name);
* }
*
* It is possible to modify default CSS class name when adding pins to the map by using
* className parameter.
*
* Please note that support for pins in the 3D map control is limited. Neither changing
* class names nor assigning actions on "over" and "out" are supported.
*
* ##Adding pins:
* ###[Simple point]
*
* $GP.map.pin.add({
* geojson: {
* "type": "Point",
* "coordinates": [-5, 51.5]
* }
* });
*
*
* ###[Feature with attributes]
*
* $GP.map.pin.add({
* geojson: {
* "type": "Feature",
* "geometry": {
* "type": "Point",
* "coordinates": [0, 51.5]
* },
* "properties": {
* "name": "London"
* }
* }
* });
*
* ###[Feature Collection]
*
* $GP.map.pin.add({
* geojson: {
* "type": "FeatureCollection",
* "features": [{
* "type": "Feature",
* "geometry": {
* "type": "Point",
* "coordinates": [13.5, 52.5]
* },
* "properties": {
* "name": "Berlin"
* }
* }, {
* "type": "Feature",
* "geometry": {
* "type": "Point",
* "coordinates": [19.5, 51.75]
* },
* "properties": {
* "name": "Lodz"
* }
* }, {
* "type": "Feature",
* "geometry": {
* "type": "Point",
* "coordinates": [24, 57]
* },
* "properties": {
* "name": "Riga"
* }
* }]
* },
* actions: {
* clicked: function () {
* },
* over: function (element) {
* element.className = "wc_map_layer_red_pin";
* },
* out: function (element) {
* element.className = "wc_map_layer_pin";
* }
* }
* });
*
* ##[Removing pins]
*
* $GP.map.pin.clear({});
*
* @class $GP.map.pin
* @singleton
*/
gp.map.pin = {
/**
* Default properties
*/
defaults: {
featureClassName: "pins",
featureClassId: "pins",
geometryFieldName: "geometry",
className: "wc_map_layer_pin"
},
_ensureFeatureClass: function(config, mapControlName) {
config = apply({}, config, this.defaults);
this._featureClasses = this._featureClasses || {};
this._featureClasses[mapControlName] = this._featureClasses[mapControlName] || {};
this._featureDatasets = this._featureDatasets || {};
this._featureDatasets[mapControlName] = this._featureDatasets[mapControlName] || new (getPortalObj(P_FEATURE_DATASET))();
var fcid = config.featureClassId,
fd = this._featureDatasets[mapControlName],
fc = this._featureClasses[mapControlName][fcid] = this._featureClasses[mapControlName][fcid] || fd.createFeatureClass({
id: fcid,
name: config.featureClassName,
geometryFieldName: config.geometryFieldName,
fields: [{
name: config.geometryFieldName,
geometryType: 1 //Intergraph.WebSolutions.Core.WebClient.Platform.Common.GeometryType.Point
}]
});
return fc;
},
_ensurePinLayer: function(config) {
// Pin layers are separate for each map control
var mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
mapControlName = mapControl.get_definitionName(),
pinLayer;
if (!this._pinLayers)
this._pinLayers = {};
var featureClass = this._ensureFeatureClass(config, mapControlName);
pinLayer = this._pinLayers[mapControlName];
if (!pinLayer){
pinLayer = this._pinLayers[mapControlName] = mapControl.getCommonPinLayer();
}
pinLayer.addFeatureClass(featureClass);
pinLayer.set_visibility(true);
return pinLayer;
},
/**
* Adding pins to the map
* Handles GeoJSON data including FeatureCollections, Features and all geometry types. When geometry
* is not a point, then point is taken from the crossing of diagonals of the bounding geometry.
* @method add
* @param {Object} config Configuration options. Either geojson or x and y or points must be provided
* @param {GeoJSON} [config.geojson] GeoJSON data
* @param {Object} [config.actions] Events on the pin
* @param {Function} [config.actions.over] What happens on the "pin mouse over"
* @param {Function} [config.actions.out] What happens on the "pin mouse out"
* @param {Function} [config.actions.clicked] What happens on the "pin mouse clicked"
* @param {String} [config.className] CSS class name of the pins
* @param {Number} [config.x] X coordinate
* @param {Number} [config.y] Y coordinate
* @param {String} [config.featureClassId] Feature Class ID
* @param {String} [config.featureClassName] Feature Class Name
* @param {String} [config.geometryFieldName] Geometry Field Name
*/
// TODO: current Portal has an issue with mouse events on pins
add: function(config, callback) {
config = apply({}, config, this.defaults);
var mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
mapControlName = mapControl.get_definitionName(),
layer = this._ensurePinLayer(config),
fc = this._featureClasses[mapControlName][config.featureClassId],
geojson = config.geojson,
actions = config.actions,
className = config.className;
if (!geojson && typeof config.x === T_NUMBER && typeof config.y === T_NUMBER)
geojson = {type: "Point", coordinates: [config.x, config.y]};
if (geojson.type === "FeatureCollection") {
var features = geojson.features || [], lastIndex = features.length - 1;
for (var i = 0, l = features.length; i < l; i++) {
var cfg = apply({}, config);
cfg.geojson = features[i];
cfg.skipfire = true;
if (i === lastIndex) {
cfg.skipfire = false;
cfg._collection = true;
}
gp.map.pin.add(cfg, callback);
}
return;
}
var pin = new Pin({
featureClass: fc,
geojson: geojson,
actions: actions,
mapStateId: config.mapStateId,
pinLayer: this._pinLayer
});
if (!config.skipfire) {
layer._pinCls = className;
layer.refresh(config.featureClassId);
fire(F_SUCCESS, callback, {
success: true,
pin: pin
});
}
},
/**
* Removing all pins
* @method clear
* @param {Object} [config] Configuration options
* @param {String} [config.featureClassId] Feature Class ID
*/
clear: function(config, callback) {
config = apply({}, config || {}, this.defaults);
var mapState = getMapState(config),
mapControl = mapState.get_mapControl(),
mapControlName = mapControl.get_definitionName(),
fid = config.featureClassId,
layer = this._ensurePinLayer(config),
fc = this._featureClasses[mapControlName][fid];
if (isSet(fid) && layer.removeFeatureClass) {
layer.removeFeatureClass(fid);
} else if (layer.removeAllFeatureClasses) {
layer.removeAllFeatureClasses();
}
fc.clearFeatures();
layer.refresh(fid);
fire(F_SUCCESS, callback, {
success: true
});
},
/**
* Sets visibility of the pin layer
* @method setVisibility
* @param {Object} config Configuration options
* @param {Boolean} config.visibility Visibility
*/
setVisibility: function(config, callback) {
config = apply({}, config || {}, this.defaults);
var layer = this._ensurePinLayer(config);
layer.set_visibility(config.visibility);
fire(F_SUCCESS, callback, {
success: true
});
}
};
/**
* Manages coordinate reference system
* @class $GP.crs
* @singleton
*/
// TODO: add helpers to gp.mapControl
gp.crs = {
_find: function (predicate, data) {
var ret = [];
if (Array.isArray(data))
for (var i = 0, l = data.length; i < l; i++)
ret = ret.concat(this._find(predicate, data[i]));
else if (Array.isArray(data.children))
ret = ret.concat(this._find(predicate, data.children));
else if (data && data.children === null)
if (predicate(data))
ret.push(data);
return ret;
},
_findAll: function (predicate, callback) {
var that = this;
var request = getPortalObj(P_WEB_REQUEST).create({
name: "CrsTree",
query: {
action: "getTree"
},
includeCRS: false,
callback: function (result) {
//TODO: check executor etc
//TODO: handle errors
var obj = result.get_object();
var ret = that._find(predicate, obj);
fire(F_SUCCESS, callback, {
success: true,
crs: ret
});
},
scope: this
});
request.invoke();
},
/**
* Finds CRS
* @method find
* @param {String/RegExp/Object} config
* @param {String} [config.value]
* @param {String} [config.regex]
* @param {Function} [config.predicate]
* @param {Function} callback
*/
find: function (config, callback) {
var code, regex, predicate;
if (typeof config === T_STRING)
code = config;
else if (typeof config === T_FUNCTION)
predicate = config;
else if (config instanceof RegExp)
regex = config;
else if (config.code)
code = config.code;
else if (config.regex)
regex = config.regex;
else if (config.predicate)
predicate = config.predicate;
if (regex)
predicate = function (o) {
return o.value.match(regex) || o.text.match(regex);
};
else if (code)
predicate = function (o) {
return o.value === code;
};
if (!predicate)
predicate = alwaysTrue;
this._findAll(predicate, callback);
},
/**
* Sets current map CRS
* @method setCurrent
* @param {Function} [config] Config
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {String/Number} callback.result.crsId Name of coordinate system which has been set.
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always "false"
* @param {String} errback.result.crsId Name of wrong crs
*/
setCurrent: function (config, callback, errback) {
// TODO: pass other options (only sane options...)
// TODO: this is sloopy
var mapStates = getPortalObj(P_MAPSTATE_MANAGER).get_mapStates();
var corrections = {};
for (var mapStateId in mapStates)
if (hasOwnProperty.call(mapStates, mapStateId))
corrections[mapStateId] = true;
var options = { correctOnlyHeightAspectRatio: corrections };
var code;
if (typeof config === T_STRING)
code = config;
else if (config)
code = config.code;
if (code === this.getCurrent())
fire(F_SUCCESS, callback, { success: true, crsId: code });
if (!code)
fire(F_FAILURE, errback, { success: false, crsId: code});
getPortalObj(P_CRS).setCurrent(code, options, function (crsChanged) {
if (crsChanged) {
getPortalObj(P_EVENT).notify(E_CANCEL_MAP_OPERATION, {}, this);
getPortalObj(P_EVENT).notify(E_CRS_CHANGED, null, this);
fire(F_SUCCESS, callback, { success: true, crsId: code });
} else {
fire(F_FAILURE, errback, { success: false, crsId: code});
}
}, this);
},
/**
* Returns current CRS
* @method getCurrent
* @return {String}
*/
getCurrent: function () {
return getPortalObj(P_CRS).getCurrent().get_id();
},
/**
* Adds CRS to the map
* @method register
* @param {Object/String} config Configuration options
* @param {String} [config.code] EPSG code
* @param {String} [config.regex] Regex for matching EPSG codes and names
* @param {Function} [config.predicate] Function for manually finding matching CRS
* @param {Function} [callback] Callback fired after CRS has been registered or in case of failure
* @return
*/
register: function (config, callback) {
var that = this, code;
if (typeof config === T_STRING)
code = config;
else if (config && config.code)
code = config.code;
if (code && code in this.getRegistered()) {
fire(F_SUCCESS, callback, { success: true, msg: "Already registered", crsIds: [code] });
return;
}
this.find(config, function (result) {
if (!result.success)
return;
var ids = [];
var crs = result.crs || [];
for (var i = 0, l = crs.length; i < l; i++) {
that._register(crs[i]);
ids.push(crs[i].value);
}
fire(F_SUCCESS, callback, { success: true, crsIds: ids });
});
},
_register: function (obj) {
var axes = obj.axisPrimary === "east" ? [2, 1] : [1, 2];
var crsStub = {
id: obj.value,
isGeographic: obj.isGeographic,
axesDirections: axes,
projAttributes: obj.attributes,
unit: obj.unit,
unitValue: obj.unitValue
};
getPortalObj(P_CRS).registerCRS(crsStub);
updateFitAllRanges(obj);
},
/**
* Returns registered CRS
* @method getRegistered
* @return {String[]}
*/
getRegistered: function () {
var ret = [], d = getPortalObj(P_CRS)._crsDict;
for (var pName in d)
if (hasOwnProperty.call(d, pName))
ret.push(pName);
return ret;
},
/**
* Transform coordinates
* @method transform
* @param {Object} config Configuration
* @param {String} [config.sourceCrsId] Source CRS identifier. If not provided, current map CRS identifier will be used
* @param {String} [config.targetCrsId] Destination CRS identifier. If not provided, current map CRS identifier will be used
* @param {Array} config.points
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {Function} [errback] callback executed if operation fails
* @param {Object} errback.result Result object
* @param {Boolean} errback.result.success Always "false"
* @param {String} errback.result.msg Reason of error, in example: "Points for transformation are not provided"
* @return
*/
transform: function (config, callback, errback) {
config = config || {};
var crsm = getPortalObj(P_CRS),
sourceCrsId = crsm.getReprCode(config.sourceCrsId || this.getCurrent()),
targetCrsId = crsm.getReprCode(config.targetCrsId),
points = config.points,
scope = this,
msg,
options = {};
if (!points)
msg = "Points for transformation are not provided";
if (msg)
fire(F_FAILURE, errback, { success: false, msg: msg});
else
crsm.transformPoints(sourceCrsId, targetCrsId, points, options, function (transformedPoints) {
if (transformedPoints)
fire(F_SUCCESS, callback, { success: true, points: transformedPoints });
else
fire(F_FAILURE, errback, { success: false, points: null, msg: "transformation failed" });
}, scope);
return scope;
},
/** Returns true if crs codes represent the same or equivalent CRS
* @method equal
* @param {String} crsCode1 first CRS code
* @param {String} ctsCode2 second CRS code
* @return {Boolean}
*/
equal: function(crsCode1, crsCode2) {
var crsm = getPortalObj(P_CRS),
repr1 = crsm.getReprCode(crsCode1),
repr2 = crsm.getReprCode(crsCode2);
return repr1 === repr2;
}
};
/**
* #User Interface
* @class $GP.ui
* @singleton
*/
gp.ui = {
infoDefaults: {
overflow: true,
showCounter: true,
timeout: 10000,
showTitle: true,
type: "info"
},
/**
* Displays UI message in portal
* @method info
* @param {Object/String} config1 configuration options or message
* @param {Object} [config2] params used when config1 is {String}
* @param {String} [config1.message] Message
* @param {String} [config1.type] Type - "info", "warning", "error"
* @param {Boolean} [config1.overflow] overflow
* @param {Boolean} [config1.showTitle] show title
* @param {Boolean} [config1.showCounter] show counter
* @param {Boolean} [config1.title] title
* @param {Boolean} [config1.timeout] timeout
* @param {String} [config2.type] Type - "info", "warning", "error"
* @param {Boolean} [config2.overflow] overflow
* @param {Boolean} [config2.showTitle] show title
* @param {Boolean} [config2.showCounter] show counter
* @param {Boolean} [config2.title] title
* @param {Boolean} [config2.timeout] timeout
* @return {void}
*/
info: function (config1, config2) {
var justTitle = typeof config1 === T_STRING,
cfg = apply({}, justTitle ? config2 : config1, this.infoDefaults),
u = getPortalObj(P_UTIL),
type = cfg.type || "info",
methodName = "show" + type.charAt(0).toUpperCase() + type.slice(1),
message = justTitle ? config1 : cfg.message;
u[methodName](message, cfg);
},
/**
* Internal web browser
* @class $GP.ui.browser
* @singleton
*/
browser: {
/**
* Returns handle to Ext object
* @method get_handle
* @return {Object} Ext handle
*/
get_handle: function () {
return getComponent(/WebBrowser1$/).get_extComponent();
},
/**
* Opens internal web browser with tab
* @method show
* @param {Object} config configuration
* @param {String} config.title tab title
* @param {String} [config.link] URL. One of link/url and html must be provided
* @param {String} [config.url] URL. One of link/url and html must be provided
* @param {String} [config.html] Raw HTML to display. One of link/url and html must be provided
* @param {Boolean} [config.showAddressBar]
* @param {Function} [callback] Function executed after the browser is shown
**/
show: function (config, callback) {
var me = this, handle = me.get_handle();
function cb () {
fire(F_SUCCESS, callback, [handle, config]);
handle.un(E_SHOW, cb, me);
}
handle.on(E_SHOW, cb, me);
getPortalObj(P_EVENT).notify(E_SHOW_WEBBROWSER, config, me);
},
/**
* Hides internal web browser
* @method show
* @param {Object} config configuration
* @param {String} [config.title] tab title. If not present whole browser will get hidden
* @param {Function} [callback] Function executed after the browser hides itself
**/
hide: function (config, callback) {
config = config || {};
var me = this, handle = me.get_handle();
function cb () {
fire(F_SUCCESS, callback, [handle, config]);
handle.un(E_HIDE, cb, me);
}
handle.on(E_HIDE, cb, me);
getPortalObj(P_EVENT).notify(E_HIDE_WEBBROWSER, config, me);
}
},
/**
* @deprecated 16.8.0
* SearchResultPanel
* @class $GP.ui.searchResultPanel
* @singleton
*/
searchResultPanel: {
/**
* Returns handle to Ext object
* @method get_handle
* @return {Object} Ext handle
*/
get_handle: function () {
return getComponent(/_SearchResultPanel1$/);
},
/**
* @deprecated 16.8.0
* Displays catalog search results using searchResultPanel.
*
* Typical usage:
* $GP.ui.searchResultPanel.displayResult({
* searchType: "apollo",
* keywords: "Cherokee"
* });
*
* @method displayResult
* @param {Object} params Search query
*/
displayResult: function (params) {
var apollo = getPortalObj(P_APOLLO);
this.get_handle().displaySearch(params, apollo.getApolloFieldDefinitions());
}
},
* Sidebar
* @class $GP.ui.sidebar
* @singleton
*/
sidebar: {
/**
* Returns handle to Ext object
* @method get_handle
* @return {Object} Ext handle
*/
get_handle: function () {
var c = getComponent(/_InnerSidebar$/) || getComponent(/_Sidebar$/);
return c && c.get_extComponent();
},
* Adds items to the sidebar.
*
* Typical usage:
* $GP.ui.sidebar.add({
* xtype: "panel",
* layout: "fit",
* title: "New Sidebar Panel",
* items: {
* xtype: "label",
* text: "Hello World"
* }
* });
*
* @method add
* @param {Object} config Ext Panel
*/
add: function (config) {
this.get_handle().add(config);
this.get_handle().doLayout();
}
},
/**
* Toolbar
* @class $GP.ui.toolbar
* @singleton
*/
toolbar: {
/**
* Returns handle to Ext object
* @method get_handle
* @return {Object} Ext handle
*/
// Provides reference to the toolbar object
get_handle: function () {
return getToolbar().get_extComponent();
},
/**
* Adds items to the toolbar.
*
* Typical usage:
* $GP.ui.toolbar.add({
* categoryIndex: 0,
* xtype: "tbbutton",
* text: "Hello World!",
* handler: function (b) {
* Ext.Msg.alert("Title", "Hello World!");
* }
* });
*
* @method add
* @param {Object} config Ext item
* @param {Number} config.categoryIndex which tab
*/
add: function (config) {
var categoryIndex = config.categoryIndex || 0;
var firstItem = this.get_handle().items.items[0],
tt = firstItem.items ? firstItem.items.items[categoryIndex].topToolbar : firstItem.topToolbar,
t = this;
if (Array.isArray(tt))
tt.push(config);
else
tt.add(config);
return t;
}
},
/**
* DataView. DataWindow or docked DataPanel
* @class $GP.ui.dataView
* @singleton
*/
dataView: {
/**
* Provides internal reference to the datawindow control
* @method get_handle
* @param {Object} config Configuration parameters
* @param {String} [config.dataWindowId] Data Window Id if it is other than default "DataWindow"
* @return {Object} Reference to ExtJS control object
*/
get_handle: function(config) {
config = config || {};
return getDataWindow(config.dataWindowId);
},
/**
* Shows data view control. Also opens new tab with feature class
* @method show
* @param {Object} config Configuration parameters
* @param {String} config.mapServiceId service ID
* @param {String} config.featureClassId feature class ID
* @param {String} [config.dataWindowId] Data Window Id if it is other than default "DataWindow"
* @param {Object} [config.options] reserved
* @return {void}
*/
show: function(config) {
var handle = this.get_handle(config);
if (!handle) return;
handle.set_hidden(false);
config = config || {};
var mapServiceId = config.mapServiceId,
featureClassId = config.featureClassId,
service = getPortalObj(P_MAPSERVICE_MANAGER).findMapService(mapServiceId),
lid = service.findLegendItemDefinition(featureClassId),
options = config.options;
handle.selectAndAdd([lid], options);
},
/**
* Hides data view control
* @method hide
* @param {Object} config Configuration parameters
* @param {String} [config.dataWindowId] Data Window Id if it is other than default "DataWindow"
* @return {void}
*/
hide: function(config) {
var handle = this.get_handle(config);
if (!handle) return;
handle.set_hidden(true);
},
_getPluginContainer: function () {
if (!this._pluginContainer) {
this._pluginContainerName = "__apiDataViewPlugins";
Type.registerNamespace(this._pluginContainerName);
/*jshint -W054 */
this._pluginContainer = new Function("return " + this._pluginContainerName)();
}
return this._pluginContainer;
},
// Hijack getRowActionButtons methods created outside API in order to be able to hide default tools
// This method is necessary because there is another way of adding row action buttons with .NET API
// 0 - BaseTools
// 1 - MapTools (no row actions)
_ensurePluginsHijacked: function () {
var dm = getPortalObj(P_DATAVIEW_MANAGER),
actionsFilter = this._actionsFilter,
actionsFilterScope = this,
overwrite = function (plugin) {
var orig = plugin.getRowActionButtons;
if (!orig) return;
// add a filter to the collection so that particular tools
// coming from other plugins can be hidden
plugin.getRowActionButtons = function(properties, scope) {
var ret = orig.call(this, properties, scope);
return ret.filter(actionsFilter, actionsFilterScope);
};
};
// TODO: refactor to use public setter after changes in Portal Core
if (!this.__hijackFlag) {
for (var i = 0, l = dm._plugins.length; i < l; i++) {
// if plugin has _actionsFilter property, it has been created with API
// and doesn't need to be overridden
if (dm._plugins[i]._actionsFilter) continue;
overwrite(dm._plugins[i]);
}
this.__hijackFlag = true;
}
},
/**
* Registers new row action
* @method registerRowAction
* @param {Object[]|Object} config Configuration options. If config is an Array, it is possible to add multiple
* row actions in a single call just by passing an array of objects described as config below.
* @param {String} config.id id of the action. By default this id would expect css class with
* the same name (icon definition) but it is possible to use inline style
* @param {String} config.text tooltip
* @param {String} config.style CSS style of the tool icon. For example "background: url(http://example.net/my/image.png);"
* @param {Function} config.predicate if the predicate returns false action is not accessible
* @param {Object} config.predicate.properties Bunch of properties
* @param {String} config.predicate.properties.mapServiceId Service ID
* @param {Boolean} config.predicate.properties.clipboard Clipboard present
* @param {Boolean} config.predicate.properties.fromClipboard action performed on clipboard
* @param {Boolean} config.predicate.properties.canDelete data source can delete
* @param {Function} config.handler Action handler
* @param {Object} config.handler.context Data context
* @param {String} config.handler.context.featureId FeatureID
* @param {String} config.handler.context.mapServiceId Service ID
* @param {String} config.handler.context.featureClassId FeatureClass ID
* @param {String} config.handler.context.id Analysis ID or feature class ID (if not analysis)
* @param {Object} config.handler.context.data Feature attributes
* @param {Boolean} config.handler.context.clipboard Clipboard present
* @param {Boolean} config.handler.context.fromClipboard action performed on clipboard
* @param {Boolean} config.handler.context.canDelete data source can delete
* @param {Object} config.handler.event Click event
* @param {Function} callback Success callback
* @param {Function} callback Failure callback
* @return {void}
*/
registerRowAction: function (descriptors, callback, errback) {
if (!Array.isArray(descriptors))
descriptors = [descriptors];
this._rowActions = this._rowActions || {};
for (var i = 0, l = descriptors.length; i < l; i++) {
var config = descriptors[i];
if (!config.id || !config.text || typeof config.handler !== T_FUNCTION)
return fire(F_FAILURE, errback, { success: false, message: "Missing parameters" });
if (this._rowActions[config.id])
return fire(F_FAILURE, errback, {
success: false,
message: "Row action with id" + config.id + " is already registered"
});
}
// create internal plugin in the plugin container
var container = this._getPluginContainer(),
cname = "P" + gp.utils.newGuid().replace(/-/g, ""),
qname = [container.getName(), cname].join("."),
scope = this;
container[cname] = function() {};
container[cname].prototype = new RowActionPlugin(descriptors, this._actionsFilter, this);
container[cname].registerClass(qname, null, getPortalObj(P_DATAVIEW_IPLUGIN));
getPortalObj(P_DATAVIEW_MANAGER).registerPlugins([qname]);
descriptors.forEach(function (d) {
this._rowActions[d.id] = cname;
}, scope);
return fire(F_SUCCESS, callback, { success: true, _classid: qname });
},
/**
* This method allows to hide row action tools by ID including default tools:
* - "wc_data_view_properties_link"
* - "wc_data_view_clipboard_link"
* - "wc_data_view_fit_link"
* - "wc_data_view_remove_from_clipboard_link"
* - "wc_data_view_remove_from_database_link"
* @method hideRowActions
* @param {String[]|String} ids IDs of the tools to be hidden
* @return {void}
*/
hideRowActions: function (ids, callback, errback) {
this._processRowActions(ids, callback, errback, true);
},
/**
* Show previously hidden row actions by ID
* @method unhideRowActions
* @param {String[]|String} ids IDs of the tools to be shown
* @return {void}
*/
unhideRowActions: function (ids, callback, errback) {
this._processRowActions(ids, callback, errback, false);
},
_processRowActions: function(ids, callback, errback, flag) {
if (!Array.isArray(ids))
ids = [ids];
this._deletedRowActions = this._deletedRowActions || {};
this._ensurePluginsHijacked();
for (var i = 0, l = ids.length; i < l; i++) {
if (typeof ids[i] !== T_STRING) {
fire(F_FAILURE, errback, { message: "Invalid argument: ids" });
return;
}
this._deletedRowActions[ids[i]] = flag;
}
fire(F_SUCCESS, callback, { ids: ids });
},
_actionsFilter: function (action) {
return !this._deletedRowActions || !this._deletedRowActions[action.id];
}
}
};
/**
* Settings
* @class $GP.settings
* @singleton
*/
gp.settings = {
/**
* Available properties
* @property available
*/
available: { "showNavigator": true, "showCrosshair": true, "showScale": true, "showCoords": true },
/**
* Returns object with settings values
* @return {Object}
*/
get: function () {
var ret = {}, settings = getPortalObj(P_SETTINGS);
for (var pName in this.available)
if (this.available[pName])
ret[pName] = settings.getValue(pName);
return ret;
},
/**
* Changes settings
* @param {Object} config Configuration object with settings
* @param {Boolean} [config.showNavigator] showNavigator
* @param {Boolean} [config.showCrosshair] showCrosshair
* @param {Boolean} [config.showScale] showScale
* @param {Boolean} [config.showCoords] showCoords
*/
set: function (config) {
config = config || {};
var i = 0, pName, settings = getPortalObj(P_SETTINGS);
for (pName in config)
if (this.available[pName]) {
settings.setValue(pName, config[pName]);
i++;
}
if (i > 0)
getPortalObj(P_EVENT).notify(E_SETTINGS_CHANGED, {}, gp.settings);
}
};
/**
* #Queries.
*
* $GP.queries is a container for executing queries in Geospatial Portal.
*
* ##Add WFS query with simple filter
*
* var queryName = "States";
*
* $GP.queries.add({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_STATES",
* url: "http://demo.hexagongeospatial.com/GWM_WFS_NonTransactional/service.svc/get",
* definitionName: "WFS",
* queryName: queryName,
* addToLegend: true,
* filters: [{
* operator: "OR",
* operands: [{
* operator: "=",
* operands: ["{http://www.intergraph.com/geomedia/gml}STATE_NAME", "Oklahoma"]
* }, {
* operator: "=",
* operands: ["{http://www.intergraph.com/geomedia/gml}STATE_NAME", "Arkansas"]
* }, {
* operator: "=",
* operands: ["{http://www.intergraph.com/geomedia/gml}STATE_NAME", "Alabama"]
* }]
* }]
* });
*
* ##Spatial filter can be easily added to a query.
* Geometries passed in spatial filter operands have to be formatted in geoJSON.
*
* var queryName = "Query with spatial filter.";
*
* $GP.queries.add({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_STATES",
* url: "http://demo.hexagongeospatial.com/GWM_WFS_NonTransactional/service.svc/get",
* definitionName: "WFS",
* queryName: queryName,
* addToLegend: true,
* filters: [{
* type: "spatial",
* operator: "Intersects",
* complement: false,
* operands: [{
* "type": "Polygon",
* "coordinates": [
* [
* [-112, 40],
* [-86, 46],
* [-80, 27],
* [-105, 27],
* [-112, 40]
* ]
* ],
* "crsId": "EPSG:4326"
* }]
* }]
* });
*
* ##Query can consist of spatial and atrribute filter.
*
* var queryName = "States";
*
* $GP.queries.add({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_STATES",
* url: "http://demo.hexagongeospatial.com/GWM_WFS_NonTransactional/service.svc/get",
* definitionName: "WFS",
* queryName: queryName,
* addToLegend: false,
* filters: [{
* operator: "OR",
* operands: [{
* operator: "=",
* operands: [
* "{http://www.intergraph.com/geomedia/gml}STATE_NAME",
* "Oklahoma"
* ]
* }, {
* operator: "=",
* operands: [
* "{http://www.intergraph.com/geomedia/gml}STATE_NAME",
* "Arkansas"
* ]
* }, {
* operator: "=",
* operands: [
* "{http://www.intergraph.com/geomedia/gml}STATE_NAME",
* "Alabama"
* ]
* }]
* }, {
* type: "spatial",
* operator: "Intersects",
* complement: false,
* operands: [{
* "type": "Polygon",
* "coordinates": [
* [
* [-112, 40],
* [-86, 46],
* [-80, 27],
* [-105, 27],
* [-112, 40]
* ]
* ],
* "crsId": "EPSG:4326"
* }]
* }]
* },
* function (result) {
* $GP.queries.find({
* analysisId: result.analysisId,
* }, function (result2) {
* result2.analysis.addToLegend({}, function () {
* $GP.legend.find({
* name: queryName
* })[0].fitLayer();
* });
* });
* });
*
* ## Combined with drawing geometry
* In order to allow user to draw geometry, $GP.queries and $GP.map.draw functions can be combined.
*
* function createAnalysis(geometry) {
* var queryName = "Query with drawing";
* $GP.queries.add({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_STATES",
* url: "http://demo.hexagongeospatial.com/GWM_WFS_NonTransactional/service.svc/get",
* definitionName: "WFS",
* queryName: queryName,
* addToLegend: true,
* filters: [{
* type: "spatial",
* operator: 'Intersects',
* complement: false,
* operands: [geometry]
*
* }]
* },
* function (result) {
* $GP.queries.find({
* analysisId: result.analysisId,
* }, function (result2) {
* result2.analysis.getData(function (data) {
* console.log(data);
* });
* });
* }
* });
*
*
* $GP.map.draw({
* "type": "Polygon" //user will be promped to draw geometry (polygon) on map
* }, function (r) {
* createAnalysis(r.feature.get_geoJSON().geometry);
* });
*
* @class $GP.queries
* @singleton
*/
gp.queries = {
/**
* Executes a query. Query is defined by passing featureClassId and filter. It is also possible to pass a style definition.
* If the `queryName` or `queryId` parameters are passed, then it is possible to update an existing analysis.
* @param {Object} config Configuration options
* @param {Boolean} [config.addToLegend=true] add query to the legend or not. It is recommended to add the analysis legend item
* with a separate addToMap call on the {Analysis} object.
* @param {String} [config.mapServiceId] map service id. Either mapServiceId
* or (url and definitionName) must be provided
* @param {String} [config.queryName] Display name of the analysis legend item. If analysis with this name already exists, it is updated. If name is not passed,
* but the query is updated by queryId, then the original name is preserved
* @param {String} [config.queryId] ID of the analysis legend item. If analysis with this id already exists, it is updated.
* @param {String} [config.url] map service url
* @param {String} [config.definitionName] map service definition name (for example "WFS")
* @param {String} [config.featureClassId] feature class ID
* @param {String} [config.applicationId] Application ID (GWMPS)
* @param {String} [config.mapStateId] map state ID
* @param {Object} [config.filter] filter
* @param {Object} [config.style] style. If style is not passed but there is already an existing legend item for this analysis meant for update,
* the original style is preserved
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.result Result object
* @param {Boolean} callback.result.success Always "true"
* @param {String} callback.result.analysisId Id of added query
* @param {Boolean} callback.result.updated "true"
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
add: function (config, callback, errback) {
config = config || {};
var addToLegend = config.addToLegend === false ? false : true,
analysisManager = getPortalObj(P_ANALYSIS_MANAGER),
existingAnalysis = analysisManager.findAnalysis(config.queryId) || analysisManager.findAnalysisByName(config.queryName),
filterLength = config.filters.length;
if (!config.mapServiceId) {
var serviceCfg = apply({}, config, {}, function(cfg, p) {
return typeof cfg[p] !== T_FUNCTION;
});
gp.services.add(serviceCfg, function (result) {
var cfg = apply({}, { mapServiceId: result.msId }, config);
gp.queries.add(cfg, callback, errback);
});
return;
}
var mapState = getMapState(config),
mapStateId = mapState.get_id(),
queryId = config.queryId || gp.utils.newGuid(),
queryName = config.queryName,
analysisStub = {
firstLegendItemDefinitionId: config.featureClassId,
id: queryId,
mapServiceId: config.mapServiceId,
name: queryName,
geometry: null,
parametrizedQueryId: null,
firstWhereClause: "",
firstWhereAttributes: [],
secondBuffer: null,
secondLegendItemDefinitionId: null,
secondWhereAttributes: [],
secondWhereClause: "",
spatialOperator: null,
spatialOperatorNegation: false,
maxFeatures: config.maxFeatures
},
options = {
callback: function (result) {
var isSuccess = result && !result.error;
fire(isSuccess ? F_SUCCESS : F_FAILURE, isSuccess ? callback : errback, {
success: isSuccess,
analysisId: isSuccess ? (result.id || analysisStub.id) : "",
updated: result.updated
}, result);
},
doNotAddToLegend: !addToLegend,
style: config.style
};
if (existingAnalysis && !queryName)
queryName = analysisStub.name = existingAnalysis.get_name();
for (var i = 0; i < filterLength ; i++) {
if (config.filters[i].type === "advanced") {
analysisStub.firstWhereClause = config.filters[0].value;
analysisStub.firstWhereAttributes = [];
} else if (config.filters[i].type === "spatial") {
var spatialFilter = config.filters[i] || {};
analysisStub.spatialOperator = spatialFilter.operator;
analysisStub.spatialOperatorNegation = !!spatialFilter.complement;
analysisStub.geometry = getPortalObj(P_GEOJSON).read(config.filters[i].operands[0]);
if (config.filters[i].operands[0].type === "BoundingBox")
analysisStub.geometry.isBBox = true;
if (gp.services.find({mapServiceId: config.mapServiceId})[0].get_definitionName() === "WFS")
analysisStub.geometry.swapCoordinates();
} else {
analysisStub.firstWhereAttributes = prepareWhereAttributes(config.filters[i]);
analysisStub.firstWhereClause = prepareQueryWhereClause(config.filters[i]);
}
}
gp.legend.find({
mapStateId: mapStateId,
id: config.queryId,
name: queryName
}, function (ret) {
if (ret.legendItems && ret.legendItems[0]) {
var internalLegendItem = ret.legendItems[0]._.li,
displayStyle = internalLegendItem.get_displayStyle();
options.style = analysisStub.style || displayStyle;
}
getPortalObj(P_ANALYSIS_MANAGER).update(mapState, analysisStub, options);
}, function () {
getPortalObj(P_ANALYSIS_MANAGER).update(mapState, analysisStub, options);
});
},
/**
* Finds Analysis
*
* $GP.queries.find({
* name: /.*$/
* }, function(ret) {
* console.log(ret.analyses)
* }, function(err) {
* console.error(err)
* });
*
* @method find
* @param {Object} config
* @param {String} [config.id] Analysis ID
* @param {String} [config.analysisId] Backward compatibility alias for id. If id is passed, it is ignored
* @param {String/RegEx} [config.name] Analysis name. it can be regex
* @param {Function} [callback] callback executed if operation succeeds
* @param {Object} callback.ret returning object
* @param {Analysis[]} callback.ret.analyses Analyses collection
* @param {Analysis} callback.ret.analysis first found Analysis (for compatibility)
* @param {Function} [errback] callback executed if operation fails or doesn't find any analysis
* @return {void}
*/
find: function (config, callback, errback) {
var analysisId = config.id || config.analysisId,
findBy = makeFindByPredicate({
key: "id",
value: analysisId
}, {
key: "name",
value: config.name
}),
predicate = config.predicate || ((isSet(analysisId) || isSet(config.name)) ? findBy : alwaysTrue),
a = getPortalObj(P_ANALYSIS_MANAGER)._analyses,
result = Object.keys(a).map(function (key) {
return {
id: key,
name: a[key].get_name()
};
}).filter(predicate).map(function (obj) {
return new Analysis({
config: {
mapStateId: config.mapStateId,
analysisId: obj.id
},
portalAnalysis: a[obj.id]
});
});
if (result.length > 0) {
fire(F_SUCCESS, callback, {
success: true,
analyses: result,
analysis: result[0] // compatibility
});
} else {
fire(F_FAILURE, errback, {
success: false,
msg: "Analysis not found."
});
}
}
};
/**
* @deprecated 16.8.0
* Object representing WPS Process
*/
function Process (config) {
this._ = {
config: config
};
var cpp = config.portalApolloProcess;
if (!cpp)
return;
this.id = cpp.id;
this.title = cpp.title;
this.abs = cpp.abs;
this.category = cpp.category;
this.inputs = cpp.inputs || [];
this.outputs = cpp.outputs|| [];
}
Process.prototype = {
/**
* Executes $GP.search with appropriate parameters so as to find items that can be input data
* to this process instance
* @method findMatchingItem
* @param {Object} [config] Configuration options
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @param {Object} [config.input] Process input for which matching datasets should be found
* @return {void}
*/
findMatchingItems: function(config, callback, errback) {
var that = this,
apolloMapServiceId = getApolloMapServiceId(),
message;
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
if(config.input.type !== "ComplexData") {
message = "Specified input does not accept complex values";
fire(F_FAILURE, errback, { success: false, message: message });
return;
}
var cswQuery = decodeURIComponent(config.input.constraint);
var processesSearchRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: {
mapServiceId: apolloMapServiceId,
action: "search",
profile: "full",
orderBy: "name asc",
cswQuery: cswQuery,
classType: !cswQuery ? "com.erdas.rsp.babel.model.imagery.ImageReference" : null
},
callback: getASR(callback, errback, "results", function(ret) {
ret.process = that;
return ret;
})
});
processesSearchRequest.invoke();
},
/**
* Executes the process
* @method execute
* @param {Object} [config] Configuration options
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @param {Object} [config.inputValues] Array of input values for the process inputs
* When inputValues are specified, their number must exactly match the number of the inputs of the process.
* @return {void}
*/
execute: function(config, callback, errback) {
var apolloMapServiceId = getApolloMapServiceId(),
that = this;
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
if(this.inputs.length !== config.inputValues.length) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_ARITY });
return;
}
var ac = getPortalObj(P_APOLLO_COMMON),
processedInputs = [];
for (var i = 0; i < this.inputs.length; i++) {
var currentInputValue;
if(this.inputs[i].type === "ComplexData")
currentInputValue = ac.prototype.prepareInputForGeoprocessing([{json: config.inputValues[i].apollo}]);
else
currentInputValue = config.inputValues[i];
processedInputs.push({
dataType: this.inputs[i].type === "ComplexData" ? "LiteralData" : this.inputs[i].type,
id: this.inputs[i].name,
value: currentInputValue || this.inputs[i].defaultValue,
valueType: this.inputs[i].type === "ComplexData" ? "complex" : null
});
}
var processesExecuteRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: {
mapServiceId: apolloMapServiceId,
action: "executeProcess",
procId: this.id,
inputs: gp.utils.serialize(processedInputs)
},
callback: handleExecutor(callback, errback, function(o) {
return o.statusLocation;
}, function(ret) {
ret.process = that;
return ret;
})
});
processesExecuteRequest.invoke();
},
/**
* Checks status of the process
* @method getStatus
* @param {Object} [config] Configuration options
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @param {Object} [config.statusLocation] Location of the process status
* @return {void}
*/
getStatus: function(config, callback, errback) {
var apolloMapServiceId = getApolloMapServiceId();
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var statusRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: {
mapServiceId: apolloMapServiceId,
action: "checkProcess",
handles: gp.utils.serialize([config.statusLocation])
},
callback: handleExecutor(callback, errback, function(o) {
return o.processes;
})
});
statusRequest.invoke();
},
/**
* Get description (details) of a specific process
* @method describe
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
describe: function(callback, errback) {
var apolloMapServiceId = getApolloMapServiceId();
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var processDescriptionRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: {
mapServiceId: apolloMapServiceId,
action: "describeProcess",
procId: this.id
},
callback: getProcesses(callback, errback)
});
processDescriptionRequest.invoke();
}
};
/**
* @deprecated 16.8.0
* #Processing
* This is a generic API for processing. Walkthrough:
*
* ##[Finding a process]
*
* $GP.processes.find(function (response) {
* $GP.ui.info("Found " + $GP.utils.serialize(response.results.length) + " processes.");
* });
*
* ##[Describing a process]
*
* $GP.processes.find(function (response) {
* var process = response.results[3];
* process.describe(function (response2) {
* $GP.ui.info("Process info: " + $GP.utils.serialize(response2.results));
* });
* });
*
* ##[Finding matching items]
*
* $GP.processes.find(function (response) {
* var process = response.results[3];
* process.describe(function (response2) {
* var process = response2.results[0];
* $GP.ui.info("Searching for matching items.");
* process.findMatchingItems({
* input: process.inputs[0]
* }, function (response3) {
* $GP.ui.info("Found " + $GP.utils.serialize(response3.results.length) + " matching items.");
* });
* });
* });
*
* ##[Executing a process and checking its status]
*
* $GP.processes.find(function (response) {
* var process = response.results[3];
* process.describe(function (response2) {
* process = response2.results[0];
* $GP.ui.info("Searching for matching items.");
* process.findMatchingItems({
* input: process.inputs[0]
* }, function (response3) {
* var matchingItem = response3.results[0];
* response3.process.execute({
* inputValues: [matchingItem]
* }, function (response4) {
* response4.process.getStatus({
* statusLocation: response4.results
* }, function (response5) {
* $GP.ui.info($GP.utils.serialize(response5.results[0]));
* });
* });
* });
* });
* });
*
* @class $GP.processes
* @singleton
*/
gp.processes = {
/**
* Finds processes available on the server.
* @method find
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
find: function(callback, errback) {
var apolloMapServiceId = getApolloMapServiceId();
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var processesSearchRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: {
mapServiceId: apolloMapServiceId,
action: "getWPSCapabilities",
refresh: true
},
callback: getProcesses(callback, errback)
});
processesSearchRequest.invoke();
},
/**
* Get history of executed processes from Apollo server
* @method history
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @return {void}
*/
history: function(callback, errback) {
var apolloMapServiceId = getApolloMapServiceId();
if(!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var processHistoryRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
body: {
mapServiceId: apolloMapServiceId,
action: "getJobs"
},
callback: handleExecutor(callback, errback, function(o) {
return o.result;
})
});
processHistoryRequest.invoke();
}
};
/**
* @deprecated 16.8.0
* #Exporting data
*
* Send ClipZipShip requests to specified email addres.
*
* Example:
*
* var data = [
* [
* [-106.55655827565818,
* 31.739841421129324, -106.55655827565818,
* 31.736170087085917, -106.5519079192032,
* 31.736170087085917, -106.5519079192032,
* 31.739841421129324, -106.55655827565818,
* 31.739841421129324
* ]
* ]
* ];
* $GP.export.clipZipShip({
* clipZipShipRequest: {
* "_encodingVersion": "2.0",
* "numberOfRequests": 1,
* "_class": "com.erdas.apollo.api.provisioning.CZSRequestContainer",
* "token": "YWRtaW46YXBvbGxvMTIz",
* "wcsUrl": "https://demo-apollo.hexagon.com/erdas-apollo/coverage_public/EAIM",
* "ignoreWarnings": true,
* "cZSRequestsSet": [{
* "name": "002_110_120830_1724__0_42656_32_0_0",
* "_class": "com.erdas.apollo.api.provisioning.CZSRequest",
* "coverageRequest": null,
* "fileOptions": null,
* "lasOptions": null,
* "requestExceptionError": null,
* "type": "RASTER",
* "imageryOptions": {
* "_class": "com.erdas.apollo.api.provisioning.CZSImageryOptions",
* "outputFormat": "IMG",
* "interpolation": "nearest neighbor",
* "measure": "Pixel",
* "outputSrs": "EPSG:26913",
* "channelAxis": "Band",
* "clipMethod": "custom-broker",
* "channels": ["band1", "band2", "band3", "band4", "band5"],
* "pixelResolutionX": 1622,
* "pixelResolutionY": 1075.1333389135348,
* "extent": {
* "srs": "EPSG:4326",
* "epsgId": 4326,
* "type": "MULTIPOLYGON",
* "cardinality": 2,
* "data": data
* }
* }
* }],
* "globalOptions": {
* "_class": "com.erdas.apollo.api.provisioning.CZSGlobalOptions",
* "clipMethod": "global-broker",
* "outputSrs": "EPSG:4326",
* "eMail": prompt("Please type in your email address"),
* "extent": {
* "srs": "EPSG:4326",
* "epsgId": 4326,
* "type": "MULTIPOLYGON",
* "cardinality": 2,
* "data": data
* }
* }
* }
* }, function callback(response) {
* $GP.ui.info($GP.utils.serialize(response));
* });
*
* @class $GP.export
* @singleton
*/
// Rhino parser in Closure Compiler treats export as a reserved keyword...
gp["export"] = {
/**
* Send ClipZipShip requests to specified email addres.
* @method clipZipShip
* @param {Object} config Configuration options
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @param {String} config.mail Target email address
* @param {Object} config.clipZipShipRequest CZS request json
* @return {void}
*/
clipZipShip: function(config, callback, errback) {
config = config || {};
var apolloMapServiceId = getApolloMapServiceId();
if (!apolloMapServiceId) {
fire(F_FAILURE, errback, { success: false, message: M_APOLLO_MISSING });
return;
}
var jsonData = {
"args": [config.clipZipShipRequest]
};
var downloadRequest = getPortalObj(P_WEB_REQUEST).create({
name: "ApolloSearch",
query: {
mapServiceId: apolloMapServiceId,
action: "download"
},
body: {
jsonData: gp.utils.serialize(jsonData)
},
includeCRS: false,
callback: handleExecutor(callback, errback, function(o) { return o; })
});
downloadRequest.invoke();
}
};
function findLegendItemDefinitionsByName(mapService, name) {
return rangerWalker({
items: mapService.get_legendItemDefinitions(),
predicate: function(lid) {
return lid.get_name() === name;
},
childrenGetter: "get_legendItemDefinitions"
});
}
function getLegendItemDefinition(lid, msid) {
var msm = getPortalObj(P_MAPSERVICE_MANAGER);
if (msid)
return msm.findMapService(msid).findLegendItemDefinition(lid);
else {
var services = msm.get_mapServices(), o;
/*jshint forin:false*/
for (var msId in services) {
if (hasOwnProperty.call(services, msId) &&
(o = services[msId].findLegendItemDefinition(lid) ||
findLegendItemDefinitionsByName(services[msId], lid)[0]))
return o;
}
}
return undefined;
}
/**
* #Selected features.
*
* ## Adding features by providing featureId and featureClassId
*
* $GP.selectedFeatures.add({
* featureId: "OM_USA_COUNTIES.1683",
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_COUNTIES"
* });
*
* ## Adding features through activating selection mode on the map
*""
* $GP.selectedFeatures.add({
* type: "Rectangle"
* }, function (result) {
* console.log(result.featureClassIds); // array of feature class IDs affected with selection
* console.log(result.featureIds); // 2-dimensional array of feature IDs. Arrays are grouped by featureClassId
* console.log(result.features); // 2-dimensional array of features. Arrays are grouped by featureClassId
* });
*
* ## Adding feature with a callback
*
* $GP.selectedFeatures.add({
* featureId: "OM_USA_COUNTIES.112",
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_COUNTIES"
* }, function (result) {
* console.log(result.success, result.featureId, result.featureClassId, result.feature.get_geoJSON());
* });
*
* ## Removing the feature
*
* $GP.selectedFeatures.clear({
* featureId: "OM_USA_COUNTIES.112",
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_COUNTIES"
* }, function (result) {
* console.log(result.success, result.featureId, result.featureClassId, result.feature.get_geoJSON());
* });
*
* ## Removing all the features
*
* $GP.selectedFeatures.clear();
*
* ## Finding features in selected set
*
* $GP.selectedFeatures.find({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_COUNTIES"
* }, function (result) {
* console.log(result)
* })
*
* @class $GP.selectedFeatures
* @singleton
*/
gp.selectedFeatures = {
/**
* Adds features to the selected set
* @param {Object} config
* @param {String} [config.featureId] Feature ID
* @param {String[]} [config.featureIds] Feature IDs
* @param {String} [config.featureClassId] FeatureClass ID
* @param {String} [config.mapServiceId] Service ID
* @param {String} [config.type] Type of the user driven selection mode. Currently
* supported types are "Point", "LineString", "Polygon" and "Rectangle"
* @param {Number[]} [config.bbox] bounding box to limit number of downloaded features.
* It can be used together with resulting bbox of $GP.map.info
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @method add
*/
add: function(config, callback, errback) {
var featureId = config.featureId,
featureIds = config.featureIds || (featureId ? [featureId] : null),
featureClassId = config.featureClassId,
mapServiceId = config.mapServiceId,
legendItemDefinition = getLegendItemDefinition(featureClassId, mapServiceId);
config.featureIds = config.featureIds || featureIds;
if (!featureIds && !featureClassId && config.type) {
return this._addByUser(config, callback, errback);
}
if (isSet(featureIds) && isSet(featureClassId)) {
var objects = featureIds.map(function(id) {
return {
id: id,
selected: true
};
});
getPortalObj(P_SELECTEDFEATURES).setSelection(legendItemDefinition, objects);
getPortalObj(P_EVENT).notify(E_SELECTEDFEATURES_CHANGED, {}, this);
return fire(F_SUCCESS, callback, {
success: true,
featureId: featureId,
featureIds: featureIds,
featureClsasId: featureClassId, // for backwards compatibility
featureClassId: featureClassId,
features: featureIds.map(function(id) {
return new Feature({ featureId: id, featureClassId: featureClassId });
}),
feature: isSet(featureId) ? new Feature({ featureId: featureId, featureClassId: featureClassId }) : undefined
});
}
return fire(F_FAILURE, errback, {
success: false,
featureClassId: featureClassId,
featureIds: featureIds
});
},
_addByUser: function (config, callback/*, errback*/) {
var e = getPortalObj(P_EVENT),
scope = this,
selectionEvent = "selectByBbox";
switch(config.type) {
case "Point":
selectionEvent = "selectByPoint";
break;
case "LineString":
selectionEvent = "selectByLine";
break;
case "Polygon":
selectionEvent = "selectByArea";
break;
case "Rectangle":
selectionEvent = "selectByBbox";
break;
}
function reg(eventName, eventArgs, sender) {
gp.selectedFeatures.find({}, function (ret) {
fire(F_SUCCESS, callback, {
success: true,
featureIds: ret.featureIds,
featureClassIds: ret.featureClassIds
});
});
unreg(eventName, eventArgs, sender);
e.unregister(E_SELECTEDFEATURES_CHANGED, reg, scope);
scope._registeredSelectedFeaturesChanged = false;
}
function unreg(eventName, eventArgs) {
if (typeof eventArgs.finishedMapOperation !== T_NUMBER)
return;
e.unregister("cancelMapOperation", unreg, scope);
//TODO: there is a risk that selectedFeaturesChanged is still registered after cancelling the selection
}
if (scope._registeredSelectedFeaturesChanged === true) {
e.unregister(E_SELECTEDFEATURES_CHANGED, reg, scope);
}
e.register(E_SELECTEDFEATURES_CHANGED, reg, scope);
scope._registeredSelectedFeaturesChanged = true;
e.register("cancelMapOperation", unreg, scope);
e.notify(selectionEvent, { mapStateId: getMapState(config).get_id() }, scope);
},
/**
* Finds all features from the given FeatureClass from the selected set
* @param {Object} config
* @param {String} [config.featureClassId] FeatureClass ID. If not passed, all selected features from all feature classes
* will be returned in the callback
* @param {Number[]} [config.bbox] bounding box to limit number of downloaded features.
* It can be used together with resulting bbox of $GP.map.info
* @param {String} [config.mapServiceId] Service ID
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @method find
*/
find: function(config, callback, errback) {
config = config || {};
var featureClassId = config.featureClassId,
allSelected = getPortalObj(P_SELECTEDFEATURES).getAllSelected(),
allSelectedMerged = Object.keys(allSelected).reduce(function (p, currentKey) {
return {
featureIds: p.featureIds.concat(allSelected[currentKey].featureIds),
legendItemDefinitions: p.legendItemDefinitions.concat(allSelected[currentKey].legendItemDefinitions)
};
}, {featureIds: [], legendItemDefinitions: []}),
index;
if (isSet(featureClassId)) {
for (var i = 0, l = allSelectedMerged.legendItemDefinitions.length; i < l; i++) {
if (allSelectedMerged.legendItemDefinitions[i].get_id() === featureClassId) {
index = i;
break;
}
}
if (!isSet(index)) {
return fire(F_FAILURE, errback, { success: false, message: "No featureClass with that featureClassId" });
}
}
var fromOneFeatureClass = isSet(featureClassId) && isSet(index),
featureClassIds = allSelectedMerged.legendItemDefinitions.map(function(lid) { return lid.get_id(); }),
features = allSelectedMerged.featureIds.map(function (subArray, i) {
return subArray.map(function(id) {
return new Feature({ featureId: id, featureClassId: featureClassIds[i] });
});
});
return fire(F_SUCCESS, callback, {
success: true,
featureIds: fromOneFeatureClass ? allSelectedMerged.featureIds[index] : allSelectedMerged.featureIds,
featureClassIds: featureClassIds,
featureClassId: fromOneFeatureClass ? featureClassIds[index] : undefined,
features: fromOneFeatureClass ? features[index]: features
});
},
/**
* Removes features from the selected set. If featureId is not passed, then
* whole selected set is cleared.
* @param {Object} config
* @param {String} [config.featureId] Feature ID
* @param {String} [config.featureClassId] FeatureClass ID
* @param {String} [config.mapServiceId] Service ID
* @param {Function} [callback] callback executed if operation succeeds
* @param {Function} [errback] callback executed if operation fails
* @method clear
*/
clear: function(config, callback, errback) {
config = config || {};
var featureId = config.featureId,
featureIds = config.featureIds || (featureId ? [featureId] : null),
lid = config.featureClassId,
msid = config.mapServiceId,
definition = getLegendItemDefinition(lid, msid);
if (isSet(featureIds) && isSet(lid)) {
var featuresToUnselect = featureIds.map(function (id) {
return {
id: id,
selected: false
};
});
getPortalObj(P_SELECTEDFEATURES).setSelection(definition, featuresToUnselect);
getPortalObj(P_EVENT).notify(E_SELECTEDFEATURES_CHANGED, {}, this);
fire(F_SUCCESS, callback, {
success: true,
featureId: featureId,
featureIds: featureIds,
featureClsasId: lid, // for backwards compatibility
featureClassId: lid
});
} else if (getPortalObj(P_SELECTEDFEATURES).clear()) {
getPortalObj(P_EVENT).notify(E_SELECTEDFEATURES_CHANGED, {}, this);
fire(F_SUCCESS, callback, { success: false });
} else {
fire(F_FAILURE, errback, { success: false });
}
}
};
function liftUserStyleCallback(mapStateId, userStyleCallback) {
return function(internalFeature) {
var publicFeature = new Feature({
portalFeature: internalFeature,
mapStateId: mapStateId
});
var publicStyleConfig = userStyleCallback(publicFeature);
return getPortalStyle(publicStyleConfig);
};
}
function liftCallback(mapStateId, userStyleCallback) {
return function (internalFeature, event) {
var publicFeature = new Feature({
portalFeature: internalFeature,
mapStateId: mapStateId
});
return userStyleCallback(publicFeature, event);
};
}
/**
* # Dynamic Styles
* With the dynamic styles you can define styling for individual features in the
* feature class. The idea is that you provide a function that returns the JSON
* style definition having a feature as its parameter.
*
* ## Simple Point Style
* This example defines custom style for features with ID lower than 1500 and uses the
* default featureClass style for others.
*
* $GP.dynamicStyles.register({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_CITIES",
* default: function (feature) {
* if (feature.get_geoJSON().properties["{http://www.intergraph.com/geomedia/gml}ID"] < 1500)
* return null;
* return {
* "style": {
* "color": "#f00",
* "name": "Red dot",
* "size": 5,
* "translucency": 0.2
* },
* defaultStyleName: "none",
* styleType: "Intergraph.WebSolutions.Core.WebClient.Platform.Style.SimplePointStyle"
* }
* }
* }, function () {
* $GP.legend.add({
* url: "http://demo.hexagongeospatial.com/GWM_WFS_NonTransactional/service.svc/get",
* definitionName: "WFS",
* ids: ["{http://www.intergraph.com/geomedia/gml}OM_USA_CITIES"]
* })
* });
*
* @class $GP.dynamicStyles
* @singleton
*/
gp.dynamicStyles = {
/**
* Registers the dynamic style callback to be used with feature classes
* with provided featureClassId so that individual features can have
* separate styling.
* Functions providing styles take feature object as a parameter and
* need to return a valid style JSON or null. If they return null, it means
* that feature class style will be used.
*""
* @param {Object} config
* @param {String} config.featureClassId FeatureClass ID
* @param {Function} [config.default] function providing the default style
* @param {Function} [config.select] function providing the select style
* @param {Function} [config.highlight] function providing the highlight style
* @param {String} [config.mapStateId] Map State ID ("map" by default)
* @param {Function} [callback] callback executed if operation succeeds
* @method register
*/
register: function (config, callback) {
var cfg = {};
["default", "highlight", "select"].forEach(function (styleName) {
if (typeof config[styleName] === T_FUNCTION)
cfg[styleName] = liftUserStyleCallback(getMapState(config).get_id(), config[styleName]);
});
getPortalObj(P_DSM).register(config.featureClassId, cfg);
fire(F_SUCCESS, callback, { success: true });
},
/**
* Unregisters the dynamic style callback
* @param {Object} config
* @param {String} config.featureClassId FeatureClass ID
* @param {String} [config.mapStateId] Map State ID ("map" by default)
* @param {Function} [callback] callback executed if operation succeeds
* @method unregister
*/
unregister: function (config, callback) {
getPortalObj(P_DSM).unregister(config.featureClassId);
fire(F_SUCCESS, callback, { success: true });
}
};
/**
* # Dynamic Feature Events
* With dynamic feature events it is possible to override default actions for particular features
* for the following events:
* * click
* * dbclick
* * tooltiptext
* * hidetooltip
* * over
* * out
*
* Dynamic feature events are defined for particular feature classes by providing the obligatory
* featureClassId parameter. Callbacks provided for the feature events have a feature object as
* their first argument. If the callback method returns true (or anything that evaluates to true)
* it means that it declares that the handling of the event is finished. If the callback returns
* null or false (or anything that evaluates to these values), it means that default action should
* be performed after the callback. An example:
*
* $GP.dynamicFeatureEvents.register({
* featureClassId: "{http://www.intergraph.com/geomedia/gml}OM_USA_STATES",
* click: function (feature) {
* if (feature.get_geoJSON().properties["{http://www.intergraph.com/geomedia/gml}ID"] % 2 === 0) {
* $GP.ui.info("With selection")
* return false;
* }
* $GP.ui.info("Without selection")
* return true;
* }
* }, function () {
* $GP.legend.add({
* url: "http://demo.hexagongeospatial.com/GWM_WFS_NonTransactional/service.svc/get",
* definitionName: "WFS",
* ids: ["{http://www.intergraph.com/geomedia/gml}OM_USA_STATES"]
* });
* });
*
* @class $GP.dynamicFeatureEvents
* @singleton
*/
gp.dynamicFeatureEvents = {
/**
* Registers dynamic feature event callback
* @param {Object} config
* @param {String} config.featureClassId FeatureClass ID
* @param {Function} [config.click]
* @param {Function} [config.dbclick]
* @param {Function} [config.tooltiptext]
* @param {Function} [config.showtooltip]
* @param {Function} [config.hidetooltip]
* @param {Function} [config.over]
* @param {Function} [config.out]
* @param {String} [config.mapStateId] Map State ID ("map" by default)
* @param {Function} [callback] callback executed if operation succeeds
* @method register
*/
register: function (config, callback) {
var cfg = {};
["click", "dbclick", "tooltiptext", "showtooltip", "hidetooltip", "over", "out"].forEach(function (eventName) {
if (typeof config[eventName] === T_FUNCTION)
cfg[eventName] = liftCallback(getMapState(config).get_id(), config[eventName]);
});
getPortalObj(P_DFEM).register(config.featureClassId, cfg);
fire(F_SUCCESS, callback, { success: true });
},
/**
* Unregisters the dynamic feature event callback
* @param {Object} config
* @param {String} config.featureClassId FeatureClass ID
* @param {String} [config.mapStateId] Map State ID ("map" by default)
* @param {Function} [callback] callback executed if operation succeeds
* @method unregister
*/
unregister: function (config, callback) {
getPortalObj(P_DFEM).unregister(config.featureClassId);
fire(F_SUCCESS, callback, { success: true });
}
};
// Converts GeoJSON feature to internal portal Feature object
// @param {Object} f - geoJSON feature
// @param {FeatureClass} - internal portal feature class
// @param {KeyField} - internal portal keyField
// @return {Feature} - internal portal feature object
function feature2feature(f, featureClass, keyField) {
var featureId = f.id || gp.utils.newGuid(keyField.get_type()),
attributes = [];
if (keyField) {
attributes.push({
Key: keyField.get_name(),
Value: featureId
});
}
var properties = f.properties || {};
for (var pName in properties) {
if (!Object.hasOwnProperty.call(properties, pName)) continue;
attributes.push({
Key: pName,
Value: properties[pName]
});
}
// fill attributes
var featureStub = {
id: featureId,
attributes: attributes
};
var feature = new (getPortalObj(P_FEATURE))(featureClass, featureStub);
if (typeof f.geometry !== T_UNDEFINED && f.geometry !== null)
feature.set_geometry(getPortalObj(P_GEOJSON).read(f.geometry));
return feature;
}
//TODO: validate presence of featureClassId parameter and existence of the featureclass
//checks whether service supports editing - parameters like for $GP.services.find
//returns service (public) and _service (private) in the callback
function validateDataEditing(config, callback, errback) {
gp.services.find(config, function (ret) {
var svc = ret.services && ret.services[0];
if (!svc) {
return fire(F_FAILURE, errback, {
success: false,
msg: "no such a service"
});
}
var ms = svc._.ms;
if (ms.get_supportsDataEditing() || ms.get_config().supportsDataEditing) {
return fire(F_SUCCESS, callback, {
success: true,
service: svc,
_service: ms
});
}
return fire(F_FAILURE, errback, {
success: false,
msg: "service does not support data editing"
});
});
}
// Collect attributes before insertion using FeatureInfoControl
// @param {Object} config
// @param {Intergraph.WebSolutions.Core.WebClient.Platform.MapServices.MapService} config.mapService internal mapService object
// @param {Intergraph.WebSolutions.Core.WebClient.Platform.MapServices.LegendItemDefinition} config.legendItemDefinition inertnal legend item definition
// @param {GeoJSON} config.feature internal feature with geometry (e.g. after drawing it)
// @param {Object} config.columnNameMapping mapping of column aliasName <-> name
// @param {Function} callback
// @param {GeoJSON} callback.feature GeoJSON.Feature
// @param {Function} errback TODO
// @return {void}
function collectFeatureAttributes(config, callback/*, errback*/) {
var mapService = config.mapService,
legendItemDefinition = config.legendItemDefinition,
toInternalFeature = config.toInternalFeature,
nextFeature = toInternalFeature(config.feature),
columnNameMapping = config.columnNameMapping;
getPortalObj(P_EVENT).notify(E_SHOW_FEATUREINFO, {
actionName: "InsertFeature",
legendItemDefinition: legendItemDefinition,
legendItemDefinitionKey: legendItemDefinition.getKey(),
wholeFeature: null,
skipRasterLayers: true,
mapServiceId: mapService.get_id(),
featureId: nextFeature && nextFeature.get_id(),
insertFeatureCallback: function (featureFromPrompt) {
// BEGIN hack - mapping with aliasName as there are no attribute column ids
var attributes = featureFromPrompt.get_attributes();
Object.keys(attributes).forEach(function (aliasName) {
if (aliasName === "_KEY_") return;
nextFeature.set_attribute(columnNameMapping[aliasName], attributes[aliasName]);
});
// END hack
fire(F_SUCCESS, callback, {
feature: getPortalObj(P_GEOJSON).getFeature(nextFeature)
});
},
insertFeatureScope: null
}, null);
}
function makeInsertFeatureCallback(config, callback/*, errback*/) {
return function() {
return fire(F_SUCCESS, callback, {
success: true,
features: config.features.map(function(f) {
var publicFeature = new Feature({
portalFeature: f,
mapStateId: config.mapStateId
});
return publicFeature;
}),
featureClassId: config.featureClassId
});
};
}
// Insert or Update features on the MapService (PSS/WFS) using GeoJSON Features
// If config.promptForAttributes is set, the Feature Info Dialog is used to collect attributes
// @param {Intergraph.WebSolutions.Core.WebClient.Platform.MapServices.MapService} internal mapService object
// @param {Object} config
// @param {Object} config.geojson - Feature or FeatureCollection
// @param {String} config.featureClassId
// @param {Boolean} [config.promptForAttributes] display FeatureInfoControl to get attributes before inserting
// @param {String} [config.mapStateId]
// @param {Function} callback
// @param {Function}
// @return {void}
function editFeature(mapService, config, callback, errback) {
var promptForAttributes = config.promptForAttributes,
geojson = config.geojson,
geojsonfeatures = geojson.features || [geojson],
idCount = geojsonfeatures.filter(function(feat) { return !!feat.id; }).length,
mapStateId = getMapState(config).get_id(),
featureClassId = config.featureClassId,
legendItemDefinition = mapService.findLegendItemDefinition(featureClassId),
beforeInsert = config.beforeInsert || function (obj, cb) { cb(obj); },
editMethod;
if (idCount === 0) {
editMethod = mapService.insertFeatures;
} else if (idCount === geojsonfeatures.length) {
editMethod = mapService.updateFeatures;
} else {
fire(F_FAILURE, errback, {
success: false,
msg: "id should be present either in all the features or not present at all"
});
return;
}
mapService.ensureColumns([legendItemDefinition], function () {
var columns = legendItemDefinition.get_columns(),
columnNameMapping = Array.prototype.reduce.call(columns, function(prev, next) {
prev[next.aliasName] = next.name;
return prev;
}, {});
mapService.getFeatureDataset([legendItemDefinition], function(emptyFeatureDataset) {
var featureClass = emptyFeatureDataset.get_featureClasses()[featureClassId],
keyField = featureClass.get_fieldContainer().getKeyFields()[0],
toInternalFeature = function (feature) {
return feature2feature(feature, featureClass, keyField);
};
collectFeatures({
mapService: mapService,
promptForAttributes: promptForAttributes,
legendItemDefinition: legendItemDefinition,
columnNameMapping: columnNameMapping,
featureClassId: featureClassId,
toInternalFeature: toInternalFeature,
geojson: config.geojson
}, function(geoJsonFeatures) {
processFeatures({
mapStateId: mapStateId,
mapServiceId: mapService.get_id(),
features: geoJsonFeatures,
toInternalFeature: toInternalFeature,
featureClassId: featureClassId,
beforeInsert: beforeInsert
}, function (features) {
var insertFeatureCallback = makeInsertFeatureCallback({
features: features,
featureClassId: featureClassId,
mapStateId: mapStateId
}, callback, errback);
editMethod.call(mapService, featureClassId, features, insertFeatureCallback, null);
}, errback);
}, errback);
});
});
}
// Preprocess GeoJSON features and transform them to internal features that can be used with mapService.insertFeature
// @param {Object} config
// @param {String} config.featureClassId
// @param {String} config.mapServiceId
// @param {GeoJSON.Feature[]} config.features
// @param {Function} config.toInternalFeature translator of GeoJSON to internal feature object
// @param {GeoJSON} config.toInternalFeature.feature GeoJSON feature
// @param {Function} config.beforeInsert
// @param {Object} config.beforeInsert.config
// @param {String} config.beforeInsert.config.featureClassId Feature Class ID
// @param {String} config.beforeInsert.config.mapServiceId Map Service ID
// @param {Function} config.beforeInsert.callback
// @param {Object} config.beforeInsert.callback.result
// @param {String} config.beforeInsert.callback.result.featureClassId Feature Class ID
// @param {String} config.beforeInsert.callback.result.mapServiceId Map Service ID
// @param {Function} config.beforeInsert.errback Error callback
// @return {void}
function processFeatures(config, callback, errback) {
var featureClassId = config.featureClassId,
mapServiceId = config.mapServiceId,
toInternalFeature = config.toInternalFeature,
geoJsonFeatures = config.features,
beforeInsert = config.beforeInsert;
beforeInsert({
features: geoJsonFeatures,
featureClassId: featureClassId,
mapServiceId: mapServiceId
}, function(ret) {
var portalFeatures = ret.features.map(toInternalFeature);
fire(F_SUCCESS, callback, portalFeatures);
}, errback);
}
// Feature Info Dialog is displayed for each feature from config.geojson if config.promptForAttributes is set
// The callback is executed after all features are ready
// @param {Object} config
// @param {Intergraph.WebSolutions.Core.WebClient.Platform.MapServices.MapService} config.mapService internal mapService object
// @param {Boolean} [config.promptForAttributes] display FeatureInfoControl to get attributes before inserting
// @param {String} config.featureClassId,
// @param {Object} config.columnNameMapping
// @param {Object} config.geojson - Feature or FeatureCollection
// @param {String} config.featureClassId
// @param {Function} callback
// @param {GeoJSON.Feature[]} callback.features Internal features
// @param {Function} errback TODO
// @return {void}
function collectFeatures(config, callback/*, errback*/) {
var mapService = config.mapService,
promptForAttributes = config.promptForAttributes,
toInternalFeature = config.toInternalFeature,
geojson = config.geojson,
legendItemDefinition = config.legendItemDefinition,
columnNameMapping = config.columnNameMapping,
features = geojson.features || [geojson],
temp,
collectedFeatures = [],
executeNextCallback = function () {
if (temp.length === 0) {
fire(F_SUCCESS, callback, collectedFeatures);
} else {
collectFeatureAttributes({
feature: temp.shift(),
toInternalFeature: toInternalFeature,
legendItemDefinition: legendItemDefinition,
mapService: mapService,
columnNameMapping: columnNameMapping
}, function (result) {
collectedFeatures.push(result.feature);
executeNextCallback();
});
}
};
if (promptForAttributes) {
temp = Array.prototype.slice.call(features);
return executeNextCallback();
} else {
return fire(F_SUCCESS, callback, features);
}
}
// Collects column information from GeoJSON features array
// To be used with Array.prototype.readuce (featureCollection.features.reduce)
function accumulateColumns (prev, current) {
for (var pName in current.properties) {
if (current.properties.hasOwnProperty(pName) && !prev.hasOwnProperty(pName)) {
if (!prev.u[pName]) {
prev.x.push({
aliasName: pName,
name: pName,
type: "System.String",
isKey: false
});
}
prev.u[pName] = !0;
}
}
return prev;
}
// Prepares internal portal FeatureDataset object from collection of simple public JSONs
// @param {Array} featureCollectionInfo - [{featureClassId: "Test", featureClassName: "Test", geojson: {/*FeatureCollection*/}}]
// @return {FeatureDataset} internal portal FeatureDataset object
function prepareFeatureDataset(featureCollectionInfo) {
var fd = new (getPortalObj(P_FEATURE_DATASET))();
for (var i = 0, l = featureCollectionInfo.length; i < l; i++) {
var data = featureCollectionInfo[i];
var fcs = getPortalObj(P_GEOJSON).readFeatureCollection(data.geojson, "geometry");
fcs.id = data.featureClassId;
fcs.name = data.featureClassName || fcs.id;
var fc = fd.createFeatureClass(fcs);
var fields = [{
name: "geometry",
geometryType: 4,
type: "Intergraph.WebSolutions.Core.WebClient.MapManager.Common.Geometry"
}].concat(data.geojson.features.reduce(accumulateColumns, {
x: [],
u: {}
}).x);
var fieldContainer = new (getPortalObj(P_FIELDCONTAINER))(fields);
fc._fieldContainer = fieldContainer;
}
return fd;
}
/**
* Feature column model
* @class Attribute
*/
function Attribute(portalColumn) {
/**
* @property {Object} _ Private internals, use at your own risk. Hic sunt leones.
* @property {Object} _.column internal portal column representation
*/
this._ = {
column: portalColumn
};
}
Attribute.prototype = {
/**
* @method get_id
* @return {String}
*/
get_id: function() { return this._.column.name; },
/**
* @method get_name
* @return {String}
*/
get_name: function() { return this._.column.aliasName; },
/**
* @method get_isKey
* @return {Boolean}
*/
get_isKey: function() { return this._.column.isKey; },
/**
* @method get_type
* @return {String}
*/
get_type: function() { return this._.column.type; }
};
// Converts internal column type to external column type
function column2column(portalColumn) {
return new Attribute(portalColumn);
}
/**
* # Data editing
* With $GP.edit it is possible to insert, update, retrieve and delete data from
* datasources that support data editing (currently it is WFS-T and PSS).
* Public API data editing is build on the basis of the GeoJSON format.
* Features are converted to internal portal representations and the portal server
* takes care of the editing.
*
* With data sources that support feature class manipulation (currently only the PSS)
* it is also possible to define, change and remove feature classes
*
* For convenience and for giving more power to the API, it is also possible to
* import whole featureClasses from GeoJSON feature collections - in that case
* featureClasses are defined by analyzing parameters in features given in the
* collection.
*
* @class $GP.edit
* @singleton
*/
gp.edit = {
/**
* Finds map service and returns its feature class IDs in a callback
* See {@link $GP.services.find} for config properties description
* @method getFeatureClassIds
* @param {Object} config
* @param {String} config.mapServiceId Map Service ID
* @param {Function} callback Success callback
* @param {Object} callback.result Result object
* @param {String[]} callback.result.featureClassIds Feature Class IDs
* @param {Function} errback Failure callback
* @return {void}
*/
getFeatureClassIds: function (config, callback, airbag) {
gp.services.find(config, function (result) {
var ms = result.services[0]._.ms,
lids = ms.get_legendItemDefinitions(),
ret = lids.map(function (x) { return x.get_id(); });
fire(F_SUCCESS, callback, {
success: true,
featureClassIds: ret
});
}, airbag);
},
/**
* Finds a feature class and returns its attributes in a callback
* See {@link $GP.services.find} for config properties description
* @method getAttributeList
* @param {Object} config
* @param {String} config.mapServiceId Map Service ID
* @param {String} config.featureClassId Feature Class ID
* @param {Function} callback Success callback
* @param {Object} callback.result Result object
* @param {String[]} callback.result.attributeList Attribute List
* @param {Function} errback Failure callback
* @return {void}
*/
getAttributeList: function (config, callback, airbag) {
config = config || {};
if (!config.featureClassId) {
return fire(F_FAILURE, airbag, {
success: false,
msg: "featureClassId not provided"
});
}
gp.services.find(config, function (result) {
var ms = result.services[0]._.ms,
lid = ms.findLegendItemDefinition(config.featureClassId);
ms.ensureColumns([lid], function () {
var columns = lid.get_columns() || [];
fire(F_SUCCESS, callback, {
attributeList: columns.map(column2column)
});
});
}, airbag);
},
/**
* @method getAttributeValues
* @param {Object} config
* @param {String} config.mapServiceId Map Service ID
* @param {String} config.featureClassId Feature Class ID
* @param {String} config.attributeId Attribute ID
* @param {Number} [config.start] Pagination start. Default = 0
* @param {Number} config.limit Pagination limit. Default = 20
* @param {Function} callback Success callback
* @param {Object} callback.result Result object
* @param {Object[]} callback.result.values Attribute List
* @param {Function} errback Failure callback
* @return {void}
*/
getAttributeValues: function (config, callback, airbag) {
config = config || {};
if ([config.mapServiceId, config.featureClassId, config.attributeId].some(function(x) {
return typeof x !== "string";
})) {
return fire(F_FAILURE, airbag, {
success: false,
msg: "featureClassId, attributeId and mapServiceId must be provided"
});
}
var request = getPortalObj(P_WEB_REQUEST).create({
name: "Analyses",
query: { action: "getvalues" },
body: {
legendItemDefinitionId: config.featureClassId,
mapServiceId: config.mapServiceId,
attributeName: config.attributeId,
start: config.start,
limit: config.limit || 20
},
includeCRS: true,
callback: function(executor) {
if (!getPortalObj(P_UTIL).checkExecutor(executor, function() {}))
return fire(F_FAILURE, airbag, { success: false });
var result = executor.get_object();
if (result.error) {
return fire(F_FAILURE, airbag, { success: false });
}
return fire(F_SUCCESS, callback, {
success: true,
values: result.values,
total: result.total
});
}
});
request.invoke();
return void(0);
}
};
/**
* Feature manipulation
* @class $GP.edit.features
* @singleton
*/
gp.edit.features = {
/**
* Adds new features to the existing feature class
* @param {Object} config
* @param {String} config.featureClassId
* @param {String} [config.mapServiceId] Service Id (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.url] URL (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.name] Service Name (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.definitionName] Service Type (either mapServiceId or definitionName&name or url must be present)
* @param {GeoJSON} config.geojson Feature or FeatureCollection.
* @param {Boolean} config.promptForAttributes collect attributes using FeatureInfo control
* @param {Function} config.beforeInsert Function executed just before the insert features request
* @param {Object} config.beforeInsert.config
* @param {String} config.beforeInsert.config.featureClassId Feature Class ID
* @param {String} config.beforeInsert.config.mapServiceId Map Service ID
* @param {Function} config.beforeInsert.callback
* @param {Object} config.beforeInsert.callback.result
* @param {String} config.beforeInsert.callback.result.featureClassId Feature Class ID
* @param {String} config.beforeInsert.callback.result.mapServiceId Map Service ID
* @param {Function} config.beforeInsert.errback Error callback
* @param {Function} callback Success callback
* @param {Object} callback.result Callback Result
* @param {Boolean} callback.result.success true
* @param {String} callback.result.featureClassId FeatureClass ID
* @param {Feature[]} callback.result.features Added features
* @param {Function} errback Failure callback
* @param {Object} errback.result Error callback result
* @param {Boolean} errback.result.success false
* @return {void}
* @method add
*/
//TODO: validate presence of geojson
//TODO: validate lack of ids
add: function (config, callback, errback) {
validateDataEditing(config, function (ret) {
editFeature(ret._service, config, callback, errback);
}, errback);
},
/**
* Finds features by ID (not Queries!)
* @param {Object} config
* @param {String} config.featureClassId
* @param {Array} config.featureIds
* @param {Function} callback
* @param {Function} errback
* @method find
*/
// TODO: WFS-T geometries are reversed
find: function (config, callback, errback) {
var mapStateId = getMapState(config).get_id();
if (!config.featureClassId || !config.featureIds) {
return fire(F_FAILURE, errback, {
success: false,
msg: "featureClassId and featureIds must be provided"
});
}
validateDataEditing(config, function (ret) {
var legendItemDefinition = ret._service.findLegendItemDefinition(config.featureClassId);
if (!legendItemDefinition) {
fire(F_FAILURE, errback, {
success: false,
msg: "Feature Class not found"
});
return;
}
ret._service.getFeatureDataset([legendItemDefinition], function (emptyFeatureDataset) {
var featureClass = emptyFeatureDataset.get_featureClasses()[config.featureClassId];
if (!featureClass.get_geometryFieldName())
featureClass._geometryFieldName = "geometry";
getPortalObj(P_WEB_REQUEST).create({
name: "InsertFeature",
query: {
action: "retrievefeatures",
mapServiceId: ret._service.get_id(),
legendItemDefinitionId: config.featureClassId
},
body: {
featureIds: JSON.stringify(config.featureIds)
},
mapService: ret._service,
callback: function(e){
var o = e.get_object();
if (!o || o.error) {
return fire(F_FAILURE, errback, {
success: false,
msg: (o || {}).error || "Unknown error"
});
}
return fire(F_SUCCESS, callback, {
success: true,
features: o.map(function(item){
var internalFeature = new (getPortalObj(P_FEATURE))(featureClass, item);
if (ret.service.definitionName === "WFS" && getPortalObj(P_CRS).qualifiesForSwapById(getPortalObj(P_CRS).getReprCode(), "WFS", null)) {
item.get_geometry.swapCoordinates();
}
var publicFeature = new Feature({
portalFeature: internalFeature,
mapStateId: mapStateId
});
return publicFeature;
}),
featureClassId: config.featureClassId
});
}
}).invoke();
}, null, {empty: true});
});
},
/**
* Update existing features by ID
* @param {Object} config
* @param {String} config.featureClassId
* @param {GeoJSON} config.geojson Feature or FeatureCollection.
* Each updated feature should have an additional "id" field:
*
* {
* "type": "Feature",
* "id": <GML_ID>
* "properties": {...},
* "geometry": {...}
* }
*
* that is used to reference the entity that is being updated.
* @param {String} [config.mapServiceId] Service Id (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.url] URL (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.name] Service Name (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.definitionName] Service Type (either mapServiceId or definitionName&name or url must be present)
* @param {Function} callback Success callback
* @param {Object} callback.result Callback Result
* @param {Boolean} callback.result.success true
* @param {String} callback.result.featureClassId FeatureClass ID
* @param {Feature[]} callback.result.features Added features
* @param {Function} errback Failure callback
* @param {Object} errback.result Error callback result
* @param {Boolean} errback.result.success false
* @return {void}
* @method update
*/
//TODO: validate presence of geojson
//TODO: validate presence of ids
update: function (config, callback, errback) {
validateDataEditing(config, function (ret) {
editFeature(ret._service, config, callback, errback);
}, errback);
},
/**
* Remove existing features by ID
* @param {Object} config
* @param {String} config.featureClassId
* @param {Array} config.featureIds
* @param {String} [config.mapServiceId] Service Id (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.url] URL (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.name] Service Name (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.definitionName] Service Type (either mapServiceId or definitionName&name or url must be present)
* @param {Function} callback
* @param {Function} errback
* @method remove
*/
remove: function (config, callback, errback) {
validateDataEditing(config, function (ret) {
var ms = ret._service,
lid = ms.findLegendItemDefinition(config.featureClassId);
ms.removeFeaturesEx([lid], [config.featureIds], function () {
getPortalObj(P_EVENT).notify("featuresRemoved", {legendItemDefinitions: [lid]});
}, null);
}, errback);
},
/**
* Imports feature collections to the service. Only for PSS now.
* @param {Object} config
* @param {String} [config.mapServiceId] Service Id (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.url] URL (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.name] Service Name (either mapServiceId or definitionName&name or url must be present)
* @param {String} [config.definitionName] Service Type (either mapServiceId or definitionName&name or url must be present)
* @param {Array} config.featureCollectionsData [{featureClassId: "Test", featureClassName: "Test", geojson: {<FeatureCollection>}}]
* @param {Function} callback
* @param {Function} errback
* @method importFeatureCollections
*/
importFeatureCollections: function (config, callback, errback) {
gp.services.find(config, function (ret) {
var fd = prepareFeatureDataset(config.featureCollectionsData);
ret.service._.ms.importFeatureDataset(fd, function (lids) {
if (lids && lids.length > 0)
fire(F_SUCCESS, callback, {
success: true,
featureClassIds: lids.map(function(lid){return lid.get_id();})
});
else
fire(F_FAILURE, errback, {
success: false
});
});
}, errback);
},
/**
* Exports feature collections from the service in JSON format. Unimplemented!
* @param {Object} config
* @param {Function} callback
* @param {Function} errback
* @method exportFeatureCollections
*/
exportFeatureCollections: function (config, callback, errback) {
gp.services.find(config, function (ret) {
var ms = ret.service._.ms,
featureClassIds = config.featureClassIds,
legendItemDefinitions,
crsId = gp.crs.getCurrent(),
crs = getPortalObj(P_CRS),
swapped = ms.get_definition().name === "WFS" && crs.qualifiesForSwapById(crsId, "WFS");
if (!featureClassIds)
legendItemDefinitions = ms.get_legendItemDefinitions().filter(function(x){return x.get_type() === 0;});
else
legendItemDefinitions = config.featureClassIds.map(function (id) {
return ms.findLegendItemDefinition(id);
});
ms.getFeatureDataset(legendItemDefinitions, makeDatasetCallback({
swapped: swapped,
format: "featureCollectionsData"
}, callback, errback));
}, errback);
}
};
/**
* PSS management
* @class $GP.edit.PSS
* @singleton
*/
gp.edit.PSS = {
/**
* Adds new PSS
* @param {Object} config
* @param {String} config.name
* @param {Array} config.featureClassDefinitions - like in addFeatureClasses
* @param {Function} callback
* @param {Function} errback
* @method add
*/
add: function (config, callback, errback) {
var d = {
supportsEditing: true,
supportsArcGeometry: true,
supportsDataEditing: true,
dataEditingClientSideOnly: false,
definitionName: "PersonalStorage",
url: ""
},
cfg = apply({}, config, d),
that = this;
gp.services.add(cfg, function (ret) {
if (config.featureClassDefinitions) {
that.addFeatureClasses({
mapServiceId: ret.msId,
featureClassDefinitions: config.featureClassDefinitions
}, function (ret2) {
fire(F_SUCCESS, callback, {
success: true,
mapServiceId: ret.msId,
msId: ret.msId,
featureClassIds: ret2.featureClassIds
});
}, errback);
} else {
fire(F_SUCCESS, callback, {
success: true,
mapServiceId: ret.msId,
msId: ret.msId,
featureClassIds: []
});
}
}, errback);
},
/**
* Adds feature classes to the existing PSS
* @param {Object} config
* @param {Array} config.featureClassDefinitions - [{featureClassId: <id>,
* [crs: <crs>], [featureClassName: <featureClassName>],
* [geometryType: <geometryType<], [fields: [{aliasName: aliasName, name: name, type: type}]]}]
* @param {Function} callback
* @param {Function} errback
* @method addFeatureClasses
*/
addFeatureClasses: function (config, callback, errback) {
if (!config.featureClassDefinitions)
return fire(F_FAILURE, errback, {success: false, msg: "no featureClassDefinitions"});
var lidstubs = [];
for (var i = 0, l = config.featureClassDefinitions.length; i < l; i++) {
var fcd = config.featureClassDefinitions[i],
id = fcd.featureClassId || createUuid(),
name = fcd.featureClassName || fcd.featureClassId,
crs = fcd.crs || gp.crs.getCurrent(),
geometryType = fcd.geometryType || 4, // Intergraph.WebSolutions.Core.WebClient.Platform.Common.GeometryType.AnySpatial,
style = fcd.style,
fields = [{
aliasName: "_KEY_",
name: "_KEY_",
type: "System.String",
isKey: true
}, {
name: "geometry",
geometryType: geometryType
}].concat(fcd.fields || []);
var fc = (new (getPortalObj(P_FIELDCONTAINER))(fields)),
lidstub = {
id: id,
name: name,
type: 0, //Intergraph.WebSolutions.Core.WebClient.Platform.MapServices.LegendItemType.Normal
validCRS: [crs],
columns: fc.serialize(),
style: {
isVisible: true,
isLocatable: true
},
canModifyData: true,
canDeleteData: true,
geometryType: geometryType,
defaultDisplayStyle: style
};
lidstubs.push(lidstub);
}
gp.services.find(config, function (ret) {
if (!ret.service)
return fire(F_FAILURE, errback, {
success: false,
msg: "No service"
});
var legendItemDefinitions = lidstubs.map(function(lidstub){return new (getPortalObj(P_LID))(lidstub, ret.service._.ms);});
ret.service._.ms.createLegendItemDefinitions(legendItemDefinitions);
return fire(F_SUCCESS, callback, {
success: true,
featureClassIds: lidstubs.map(function(s){return s.id;})
});
}, errback);
},
/**
* Removes featureClass from the PSS Unimplemented
* @param {Object} config
* @param {Function} callback
* @param {Function} errback
* @method add
*/
removeFeatureClass: function (config, callback, errback) {
var featureClassId = config.featureClassId;
if (!featureClassId) {
fire(F_FAILURE, errback, {
success: false,
msg: "No featureClassId"
});
}
this.find(config, function(ret) {
if (!ret.service) {
fire(F_FAILURE, errback, {
success: false,
msg: "No service"
});
return;
}
var portalMapService = ret.service._.ms,
lid = portalMapService.findLegendItemDefinition(featureClassId),
legendItemDefinitionsRemoved = function(eventName, eventArgs, sender) {
if (sender !== portalMapService) return;
fire(F_SUCCESS, callback, {
success: true,
featureClassId: featureClassId,
_: {
legendItemDefinitions: eventArgs.legendItemDefinitions
}
});
getPortalObj(P_EVENT).unregister("legendItemDefinitionsRemoved", legendItemDefinitionsRemoved, this);
};
if (!lid) {
fire(F_FAILURE, errback, {
success: false,
msg: "Feature Class not found"
});
return;
}
portalMapService.removeLegendItemDefinitions([lid]);
getPortalObj(P_EVENT).register("legendItemDefinitionsRemoved", legendItemDefinitionsRemoved, this);
}, errback);
},
/**
* Convenience method for finding PSS service
* @param {Object} config
* @param {Function} callback
* @param {Function} errback
* @method find
*/
find: function (config, callback, errback) {
var cfg = apply({}, config, { definitionName: "PersonalStorage" });
gp.services.find(cfg, callback, errback);
},
/**
* Removes the PSS
* @param {Object} config
* @param {Function} callback
* @param {Function} errback
* @method remove
*/
remove: function (config, callback, errback) {
this.find(config, function (ret) {
var svc = ret.service;
if (svc) {
svc.remove(callback, errback);
} else
fire(F_FAILURE, errback, {
success: false,
msg: "No such a service"
});
}, errback);
},
/**
* Creates new PSS from featureCollections
* @param {Object} config
* @param {String} config.name Name of the PSS to be created
* @param {Array} config.featureCollectionsData [{featureClassId: "Test", featureClassName: "Test", geojson: {<FeatureCollection>}}]
* @param {Function} callback
* @param {Function} errback
* @method createFromFeatureCollections
*/
createFromFeatureCollections: function (config, callback, errback) {
this.add({
name: config.name
}, function (ret) {
gp.edit.features.importFeatureCollections({
mapServiceId: ret.mapServiceId,
featureCollectionsData: config.featureCollectionsData
}, function (ret2) {
gp.legend.add({
mapServiceId: ret.mapServiceId,
ids: ret2.featureClassIds
}, callback, errback);
}, errback);
}, errback);
}
};
/**
* Vendor Specific Parameters Manager
* @class $GP.vspm
* @singleton
*
* Examples:
*
* Add "Hello=World" GET parameter to each WMS GetMap request:
*
* $GP.vspm({
* handler: function (context) { return {"Hello": "World"}; },
* predicate: function (context) { return context.operation === "GetMap" && context.definitionName === "WMS"; }
* },
* function (ret) {
* console.log(ret.id);
* });
*
*/
gp.vspm = {
/**
* Registers new Vendor Specific Parameter
* @param {Object} descriptor handler descriptor
* @param {Function} descriptor.handler function returning dictionary of vendor specific parameters for the given context
* @param {Object} descriptor.handler.context execution context
* @param {String} descriptor.handler.context.operation operation - "GetMap", "Transaction" etc
* @param {String} descriptor.handler.context.definitionName definition name - "WMS", "WFS" etc
* @param {String} descriptor.handler.context.mapServiceId Map Service ID
* @param {String} descriptor.handler.context.mapServiceUrl Map Service URL
* @param {Object} descriptor.handler.context.return Dictionary of Vendor Specific Parameters
* @param {Function} descriptor.predicate predicate for addressing the handler only for particular contexts
* @param {Object} descriptor.predicate.context execution context
* @param {String} descriptor.predicate.context.operation operation - "GetMap", "Transaction" etc
* @param {String} descriptor.predicate.context.definitionName definition name - "WMS", "WFS" etc
* @param {String} descriptor.predicate.context.mapServiceId Map Service ID
* @param {String} descriptor.predicate.context.mapServiceUrl Map Service URL
* @param {Boolean} descriptor.predicate.context.return returns true if the handler is to be executed for the given context
* @param {Function} callback callback executed when the operation is successfull
* @param {Object} callback.ret return value of the callback
* @param {String} callback.ret.id id of the registered handler
* @param {Function} errback callback executed when the operation is not successfull
* @param {Object} errback.ret return value of the callback
* @param {String} errback.ret.message error message
* @return {void}
* @method register
*/
register: function (descriptor, callback, errback) {
try {
if (typeof descriptor.handler !== "function") throw new Error("handler must be a function");
var id = getPortalObj(P_VSPM).register(descriptor);
this.ids = this.ids || [];
this.ids.push(id);
fire(F_SUCCESS, callback, { id: id });
} catch (e) {
fire(F_FAILURE, errback, { message: e });
}
},
/**
* Unregisters particular VSP handler(s). If executed without id, it unregisters all the handlers
* @param {String} id ID of the handler to be unregistered
* @param {Function} callback callback executed when the operation is successfull
* @param {Object} callback.ret return value of the callback
* @param {String} callback.ret.ids ids of the unregistered handlers
* @param {String} callback.ret.id id of the unregistered handler
* @param {Function} errback callback executed when the operation is not successfull
* @param {Object} errback.ret return value of the callback
* @param {String} errback.ret.id id of the the handler that failed to be unregistered
* @return {void}
* @method unregister
*/
unregister: function (id, callback, errback) {
var idsToUnregister;
try {
id = id || this.ids;
idsToUnregister = Array.isArray(id) ? id : [id];
var vspm = getPortalObj(P_VSPM);
var ret = idsToUnregister.map(vspm.unregister, vspm);
if (ret.some(function (x) { return x !== true; })) throw new Error("Unable to unregister handler");
this.ids = this.ids.filter(function (x) { return idsToUnregister.indexOf(x) === -1; });
fire(F_SUCCESS, callback, { ids: idsToUnregister, id: idsToUnregister[0] });
} catch (e) {
fire(F_FAILURE, errback, { message: e, id: id });
}
},
/**
* Returns the VSP for debugging purposes
* @param {Object} context execution context
* @param {String} context.operation operation - "GetMap", "Transaction" etc
* @param {String} handler.context.definitionName definition name - "WMS", "WFS" etc
* @param {String} context.mapServiceId Map Service ID
* @param {String} context.mapServiceUrl Map Service URL
* @return {Object} paremeters for the given execution context
*/
getParameters: function (context) {
return getPortalObj(P_VSPM).getParameters(context);
}
};
/**
* Vendor Specific Parameters Manager
* @class $GP.vspm
* @singleton
*
* Examples:
*
* Add "Hello=World" GET parameter to each WMS GetMap request:
*
* $GP.vspm({
* handler: function (context) { return {"Hello": "World"}; },
* predicate: function (context) { return context.operation === "GetMap" && context.definitionName === "WMS"; }
* },
* function (ret) {
* console.log(ret.id);
* });
*
*/
gp.vspm = {
/**
* Registers new Vendor Specific Parameter
* @param {Object} descriptor handler descriptor
* @param {Function} descriptor.handler function returning dictionary of vendor specific parameters for the given context
* @param {Object} descriptor.handler.context execution context
* @param {String} descriptor.handler.context.operation operation - "GetMap", "Transaction" etc
* @param {String} descriptor.handler.context.definitionName definition name - "WMS", "WFS" etc
* @param {String} descriptor.handler.context.mapServiceId Map Service ID
* @param {String} descriptor.handler.context.mapServiceUrl Map Service URL
* @param {Object} descriptor.handler.context.return Dictionary of Vendor Specific Parameters
* @param {Function} descriptor.predicate predicate for addressing the handler only for particular contexts
* @param {Object} descriptor.predicate.context execution context
* @param {String} descriptor.predicate.context.operation operation - "GetMap", "Transaction" etc
* @param {String} descriptor.predicate.context.definitionName definition name - "WMS", "WFS" etc
* @param {String} descriptor.predicate.context.mapServiceId Map Service ID
* @param {String} descriptor.predicate.context.mapServiceUrl Map Service URL
* @param {Boolean} descriptor.predicate.context.return returns true if the handler is to be executed for the given context
* @param {Function} callback callback executed when the operation is successfull
* @param {Object} callback.ret return value of the callback
* @param {String} callback.ret.id id of the registered handler
* @param {Function} errback callback executed when the operation is not successfull
* @param {Object} errback.ret return value of the callback
* @param {String} errback.ret.message error message
* @return {void}
* @method register
*/
register: function (descriptor, callback, errback) {
try {
if (typeof descriptor.handler !== "function") throw new Error("handler must be a function");
var id = getPortalObj(P_VSPM).register(descriptor);
this.ids = this.ids || [];
this.ids.push(id);
fire(F_SUCCESS, callback, { id: id });
} catch (e) {
fire(F_FAILURE, errback, { message: e });
}
},
/**
* Unregisters particular VSP handler(s). If executed without id, it unregisters all the handlers
* @param {String} id ID of the handler to be unregistered
* @param {Function} callback callback executed when the operation is successfull
* @param {Object} callback.ret return value of the callback
* @param {String} callback.ret.ids ids of the unregistered handlers
* @param {String} callback.ret.id id of the unregistered handler
* @param {Function} errback callback executed when the operation is not successfull
* @param {Object} errback.ret return value of the callback
* @param {String} errback.ret.id id of the the handler that failed to be unregistered
* @return {void}
* @method unregister
*/
unregister: function (id, callback, errback) {
var idsToUnregister;
try {
id = id || this.ids;
idsToUnregister = Array.isArray(id) ? id : [id];
var vspm = getPortalObj(P_VSPM);
var ret = idsToUnregister.map(vspm.unregister, vspm);
if (ret.some(function (x) { return x !== true; })) throw new Error("Unable to unregister handler");
this.ids = this.ids.filter(function (x) { return idsToUnregister.indexOf(x) === -1; });
fire(F_SUCCESS, callback, { ids: idsToUnregister, id: idsToUnregister[0] });
} catch (e) {
fire(F_FAILURE, errback, { message: e, id: id });
}
},
/**
* Returns the VSP for debugging purposes
* @param {Object} context execution context
* @param {String} context.operation operation - "GetMap", "Transaction" etc
* @param {String} handler.context.definitionName definition name - "WMS", "WFS" etc
* @param {String} context.mapServiceId Map Service ID
* @param {String} context.mapServiceUrl Map Service URL
* @return {Object} paremeters for the given execution context
*/
getParameters: function (context) {
return getPortalObj(P_VSPM).getParameters(context);
}
};
})(window);