Closed tscharff closed 9 years ago
thanks for taking the time to report your findings. this is because map.geographicExtent (in the API itself) is only available for web mercator applications. the relevant code in the template is here
Its possible to enhance to add similar support for other coordinate systems, but it would require a fairly significant refactor and an external request to a external geometry service to retrieve lat/longs.
Thank you for the quick reply.
Yeah, that was my guess, but there were 2 contributing factors that made it less clear: I couldn't find documentation about it for the Basic Viewer (and Classic Viewer); and the share function works as expected for the webmap, itself (inside the map viewer).
We are trying to deploy an application that will display capital projects throughout a service area. Our webmaster would like to launch the app from a list of projects... when the user clicks on a project, the app will open with the map automatically zoomed to the project area. I was originally trying to find a way to zoom to a feature using URL parameters, but was willing to use the extent as a workaround. Without the capability, the webmaster is proposing to use Google maps.
If URL parameters for zoom to extent represents a significant effort for other coordinate systems, perhaps the effort to implement URL parameters for zoom to feature would require much less effort (since the app is already capable of zooming to features via context menu and pop-ups, etc).
Below are a few links to related ArcGIS Ideas... it appears there is support from the user community to add related functionality.
ArcGIS Idea for consistent share function... https://c.na9.visual.force.com/apex/ideaView?id=087E00000005Aw7IAE
ArcGIS Ideas for URL parameters to zoom to feature... https://c.na9.visual.force.com/apex/ideaView?id=087E00000005IDR&returnUrl=%2Fapex%2FideaList%3Fc%3D09a300000004xET%26category%3DArcGIS%2BOnline
we appreciate the feedback @tscharff. that type of functionality could make its way into the Viewer in a future release, but the code itself is extensible and written using your JavaScript API so you are welcome to incorporate it in your own project immediately.
the code i referenced in my earlier reply demonstrates how to parse values from a url query so please feel free to use it as an example if you'd like to incorporate your own alternative custom logic.
@tscharff There may be some options for working around this issue I'd just like to understand a bit more about your map. Are you using the hosted template or have you downloaded the code and have it hosted on your own web server? Do you add your locally hosted service to the map after its created?
We are using a hosted template.
I created a new webmap that uses a locally hosted (non ArcGIS.com) basemap, and that also contains our locally hosted service containing the capital projects. (Both services are in a state plane projection.) Afterward, I saved the map, and then created the web map application based on a template.
Hope that helps. If you need more info, please let me know. Thanks!
@tscharff I have an updated version of ShareDialog.js that I think will work for your situation. Can you test and let me know? If it does work for you I'll work on incorporating this logic (after more testing on my end) into the Viewer template for the next release.
define(["dojo/Evented", "dojo/_base/declare", "dojo/Deferred", "dojo/_base/lang", "dojo/has", "esri/kernel", "esri/config", "esri/SpatialReference", "dijit/_WidgetBase", "dijit/a11yclick", "dijit/_TemplatedMixin", "dojo/on",
// load template
"dojo/text!application/dijit/templates/ShareDialog.html", "dojo/i18n!application/nls/ShareDialog", "dojo/dom-class", "dojo/dom-style", "dojo/dom-attr", "dojo/dom-construct", "esri/request", "esri/urlUtils", "dijit/Dialog", "dojo/number", "dojo/_base/event"], function (
Evented, declare, Deferred, lang, has, esriNS, esriConfig, SpatialReference, _WidgetBase, a11yclick, _TemplatedMixin, on, dijitTemplate, i18n, domClass, domStyle, domAttr, domConstruct, esriRequest, urlUtils, Dialog, number, event) {
var Widget = declare("esri.dijit.ShareDialog", [_WidgetBase, _TemplatedMixin, Evented], {
templateString: dijitTemplate,
options: {
theme: "ShareDialog",
dialog: null,
useExtent: false,
map: null,
url: window.location.href,
image: "",
title: window.document.title,
summary: "",
hashtags: "",
mailURL: "mailto:%20?subject={title}&body={summary}%20{url}",
facebookURL: "https://www.facebook.com/sharer/sharer.php?s=100&p[url]={url}&p[images][0]={image}&p[title]={title}&p[summary]={summary}",
twitterURL: "https://twitter.com/intent/tweet?url={url}&text={title}&hashtags={hashtags}",
googlePlusURL: "https://plus.google.com/share?url={url}",
bitlyAPI: location.protocol === "https:" ? "https://api-ssl.bitly.com/v3/shorten" : "http://api.bit.ly/v3/shorten",
bitlyLogin: "",
bitlyKey: "",
embedSizes: [{
"width": "100%",
"height": "640px"
},
{
"width": "100%",
"height": "480px"
},
{
"width": "100%",
"height": "320px"
},
{
"width": "800px",
"height": "600px"
},
{
"width": "640px",
"height": "480px"
},
{
"width": "480px",
"height": "320px"
}]
},
// lifecycle: 1
constructor: function (options, srcRefNode) {
// mix in settings and defaults
var defaults = lang.mixin({}, this.options, options);
// widget node
this.domNode = srcRefNode;
this._i18n = i18n;
// properties
this.set("theme", defaults.theme);
this.set("url", defaults.url);
this.set("visible", defaults.visible);
this.set("dialog", defaults.dialog);
this.set("embedSizes", defaults.embedSizes);
this.set("embedHeight", defaults.embedHeight);
this.set("embedWidth", defaults.embedWidth);
this.set("mailURL", defaults.mailURL);
this.set("facebookURL", defaults.facebookURL);
this.set("twitterURL", defaults.twitterURL);
this.set("googlePlusURL", defaults.googlePlusURL);
this.set("bitlyAPI", defaults.bitlyAPI);
this.set("bitlyLogin", defaults.bitlyLogin);
this.set("bitlyKey", defaults.bitlyKey);
this.set("image", defaults.image);
this.set("title", defaults.title);
this.set("summary", defaults.summary);
this.set("hashtags", defaults.hashtags);
this.set("useExtent", defaults.useExtent);
// listeners
this.watch("theme", this._updateThemeWatch);
this.watch("url", this._updateUrl);
this.watch("visible", this._visible);
this.watch("embedSizes", this._setSizeOptions);
this.watch("embed", this._updateEmbed);
this.watch("bitlyUrl", this._updateBitlyUrl);
this.watch("useExtent", this._useExtentChanged);
// classes
this.css = {
container: "button-container",
embed: "embed-page",
button: "toggle-grey",
buttonSelected: "toggle-grey-on",
icon: "icon-share",
linkIcon: "icon-link share-dialog-icon",
facebookIcon: "icon-facebook-squared share-dialog-icon",
twitterIcon: "icon-twitter share-dialog-icon",
gplusIcon: "icon-gplus share-dialog-icon",
emailIcon: "icon-mail-alt share-dialog-icon",
mapSizeLabel: "map-size-label",
shareMapURL: "share-map-url",
iconContainer: "icon-container",
embedMapSizeDropDown: "embed-map-size-dropdown",
shareDialogContent: "dialog-content",
shareDialogSubHeader: "share-dialog-subheader",
shareDialogTextarea: "share-dialog-textarea",
shareDialogExtent: "share-dialog-extent",
shareDialogExtentChk: "share-dialog-checkbox",
mapSizeContainer: "map-size-container",
embedMapSizeClear: "embed-map-size-clear",
iconClear: "icon-clear"
};
},
// bind listener for button to action
postCreate: function () {
this.inherited(arguments);
this._setExtentChecked();
this._shareLink();
this.own(on(this._extentInput, a11yclick, lang.hitch(this, this._useExtentUpdate)));
},
// start widget. called by user
startup: function () {
this._init();
},
// connections/subscriptions will be cleaned up during the destroy() lifecycle phase
destroy: function () {
this.inherited(arguments);
},
/* ---------------- */
/* Public Events */
/* ---------------- */
// load
/* ---------------- */
/* Public Functions */
/* ---------------- */
/* ---------------- */
/* Private Functions */
/* ---------------- */
_setExtentChecked: function () {
domAttr.set(this._extentInput, "checked", this.get("useExtent"));
},
_useExtentUpdate: function () {
var value = domAttr.get(this._extentInput, "checked");
this.set("useExtent", value);
},
_useExtentChanged: function () {
this._updateUrl();
this._shareLink();
},
_setSizeOptions: function () {
// clear select menu
this._comboBoxNode.innerHTML = "";
// if embed sizes exist
if (this.get("embedSizes") && this.get("embedSizes").length) {
// map sizes
for (var i = 0; i < this.get("embedSizes").length; i++) {
if (i === 0) {
this.set("embedWidth", this.get("embedSizes")[i].width);
this.set("embedHeight", this.get("embedSizes")[i].height);
}
var option = domConstruct.create("option", {
value: i,
innerHTML: this.get("embedSizes")[i].width + " x " + this.get("embedSizes")[i].height
});
domConstruct.place(option, this._comboBoxNode, "last");
}
}
},
_updateUrl: function () {
// nothing currently shortened
this._shortened = null;
// no bitly shortened
this.set("bitlyUrl", null);
// vars
var map = this.get("map"),
url = this.get("url"),
useSeparator;
// get url params
var urlObject = urlUtils.urlToObject(window.location.href);
urlObject.query = urlObject.query || {};
urlObject.query.extent = null;
// include extent in url
/* if (this.get("useExtent") && map) {
this._projectGeometry(map);
}*/
this._projectGeometry().then(lang.hitch(this, function (result) {
if (result) {
var gExtent = result;
urlObject.query.extent = gExtent.xmin.toFixed(4) + ',' + gExtent.ymin.toFixed(4) + ',' + gExtent.xmax.toFixed(4) + ',' + gExtent.ymax.toFixed(4);
}
// create base url
url = window.location.protocol + "//" + window.location.host + window.location.pathname;
// each param
for (var i in urlObject.query) {
if (urlObject.query[i]) {
// use separator
if (useSeparator) {
url += "&";
} else {
url += "?";
useSeparator = true;
}
url += i + "=" + urlObject.query[i];
}
}
// update url
this.set("url", url);
// reset embed code
this._setEmbedCode();
// set url value
domAttr.set(this._shareMapUrlText, "value", url);
domAttr.set(this._linkButton, "href", url);
}));
},
_projectGeometry: function () {
var deferred = new Deferred();
var map = this.get("map");
if (this.get("useExtent") && map) {
// get map extent in geographic
if (map.geographicExtent) {
deferred.resolve(map.geographicExtent);
// var gExtent = map.geographicExtent;
// set extent string
//urlObject.query.extent = gExtent.xmin.toFixed(4) + ',' + gExtent.ymin.toFixed(4) + ',' + gExtent.xmax.toFixed(4) + ',' + gExtent.ymax.toFixed(4);
} else {
//project the extent to geographic
var outSR = new SpatialReference({
"wkid": 4326
});
esriConfig.defaults.geometryService.project([map.extent], outSR).then(lang.hitch(this, function (result) {
if (result.length) {
var projectedExtent = result[0];
deferred.resolve(projectedExtent);
//urlObject.query.extent = projectedExtent.xmin.toFixed(4) + ',' + projectedExtent.ymin.toFixed(4) + ',' + projectedExtent.xmax.toFixed(4) + ',' + projectedExtent.ymax.toFixed(4);
}
}));
}
} else {
deferred.resolve(null);
}
return deferred.promise;
},
_init: function () {
// set sizes for select box
this._setSizeOptions();
var dialog = new Dialog({
title: i18n.widgets.ShareDialog.title,
draggable: false
}, domConstruct.create("div"));
this.set("dialog", dialog);
// set embed url
this._updateUrl();
// select menu change
this.own(on(this._comboBoxNode, "change", lang.hitch(this, function (evt) {
this.set("embedWidth", this.get("embedSizes")[parseInt(evt.currentTarget.value, 10)].width);
this.set("embedHeight", this.get("embedSizes")[parseInt(evt.currentTarget.value, 10)].height);
this._setEmbedCode();
})));
// facebook click
this.own(on(this._facebookButton, a11yclick, lang.hitch(this, function () {
this._configureShareLink(this.get("facebookURL"));
})));
// twitter click
this.own(on(this._twitterButton, a11yclick, lang.hitch(this, function () {
this._configureShareLink(this.get("twitterURL"));
})));
// google plus click
this.own(on(this._gpulsButton, a11yclick, lang.hitch(this, function () {
this._configureShareLink(this.get("googlePlusURL"));
})));
// email click
this.own(on(this._emailButton, a11yclick, lang.hitch(this, function () {
this._configureShareLink(this.get("mailURL"), true);
})));
// link box click
this.own(on(this._shareMapUrlText, a11yclick, lang.hitch(this, function () {
this._shareMapUrlText.setSelectionRange(0, 9999);
})));
// link box mouseup stop for touch devices
this.own(on(this._shareMapUrlText, "mouseup", function (evt) {
event.stop(evt);
}));
// embed box click
this.own(on(this._embedNode, a11yclick, lang.hitch(this, function () {
this._embedNode.setSelectionRange(0, 9999);
})));
// embed box mouseup stop for touch devices
this.own(on(this._embedNode, "mouseup", function (evt) {
event.stop(evt);
}));
// loaded
this.set("loaded", true);
this.emit("load", {});
},
_updateEmbed: function () {
domAttr.set(this._embedNode, "value", this.get("embed"));
},
_setEmbedCode: function () {
var es = "<iframe width='" + this.get("embedWidth") + "' height='" + this.get("embedHeight") + "' src='" + this.get("url") + "' frameborder='0' scrolling='no'></iframe>";
this.set("embed", es);
},
_updateBitlyUrl: function () {
var bitly = this.get("bitlyUrl");
if (bitly) {
domAttr.set(this._shareMapUrlText, "value", bitly);
domAttr.set(this._linkButton, "href", bitly);
}
},
_shareLink: function () {
if (this.get("bitlyAPI") && this.get("bitlyLogin") && this.get("bitlyKey")) {
var currentUrl = this.get("url");
// not already shortened
if (currentUrl !== this._shortened) {
// set shortened
this._shortened = currentUrl;
// make request
esriRequest({
url: this.get("bitlyAPI"),
callbackParamName: "callback",
content: {
uri: currentUrl,
login: this.get("bitlyLogin"),
apiKey: this.get("bitlyKey"),
f: "json"
},
load: lang.hitch(this, function (response) {
if (response && response.data && response.data.url) {
this.set("bitlyUrl", response.data.url);
}
}),
error: function (error) {
console.log(error);
}
});
}
}
},
_configureShareLink: function (Link, isMail) {
// replace strings
var fullLink = lang.replace(Link, {
url: encodeURIComponent(this.get("bitlyUrl") ? this.get("bitlyUrl") : this.get("url")),
image: encodeURIComponent(this.get("image")),
title: encodeURIComponent(this.get("title")),
summary: encodeURIComponent(this.get("summary")),
hashtags: encodeURIComponent(this.get("hashtags"))
});
// email link
if (isMail) {
window.location.href = fullLink;
} else {
window.open(fullLink, "share", true);
}
}
});
if (has("extend-esri")) {
lang.setObject("dijit.ShareDialog", Widget, esriNS);
}
return Widget;
});
Sorry for the late reply... I've been out sick for about 1.5 weeks, and still only back on a very part-time basis.
We were using a hosted template, but when I return to work I will download the code and test the ShareDialog.js that you provided above. Thanks!
In a scenario where a webmap contains a locally hosted (non ArcGIS.com) map service that is in a different projected coordinate system, the Basic Viewer Share tool does not update the URL (in Map Link or Embed Map) to include the map extent. (even though the box for "Share current map extent" is checked)
While troubleshooting the issue, I created a locally hosted map service that is in Web Mercator Auxiliary Sphere (and a corresponding webmap in ArcGIS.com). In this scenario the Share Tool generates the URL correctly, and allows the Basic Viewer to correctly zoom to the specified map extent when the map is shared.