I'm tryng to create a route planner to track my running routes. Using Bing Maps, I am able to create the route, but I'm struggling to remove to default 'beginning', 'end' and 'red circle' itinery icons.
Below is my code so far (based on this link). All I basically want is my own start icon at the beginning of the route and my end icon at the end. I don't need anything else in between apart from the route line.
Any help (along with code improvement tips) gratefully received!
jQuery(function() {
GetMap();
$("#btnStartRoute").click(function() {
map.AttachEvent('onclick', StartRouting);
});
});
var map = null;
var myRoute = [];
var noOfPushPins = 0;
function GetMap() {
map = new VEMap('mapContent');
map.SetCredentials("xxxxxxxxxxxxxxxxxx");
map.LoadMap();
}
function StartRouting(e) {
var xPoint = e.mapX, yPoint = e.mapY;
var pixel = new VEPixel(xPoint, yPoint);
var LL = map.PixelToLatLong(pixel);
cornerOne = LL; //cornerOne is a global level var
var latitude = map.PixelToLatLong(pixel).Latitude;
var longitiude = map.PixelToLatLong(pixel).Longitude;
myRoute[noOfPushPins] = new VELatLong(latitude, longitiude);
noOfPushPins++;
GetRoute();
}
function GetRoute() {
var myRouteOptions = new VERouteOptions();
myRouteOptions.RouteMode = VERouteMode.Walking;
myRouteOptions.RouteColor = new VEColor(0, 102, 51, .7);
myRouteOptions.RouteCallback = RouteCallback;
map.GetDirections(myRoute, myRouteOptions);
}
function RouteCallback(route) {
var myRouteShapes = [];
var myRoutePoints = [];
var points = route.RouteLegs[0].Itinerary.Items;
$.each(points, function(i) {
var routePointCoordinates = new VELatLong(route.RouteLegs[0].Itinerary.Items[i].LatLong.Latitude, route.RouteLegs[0].Itinerary.Items[i].LatLong.Longitude);
var routePointShape = new VEShape(VEShapeType.Pushpin, routePointCoordinates);
if (i != 0) {
routePointShape.SetCustomIcon("<img id='pushPin" + noOfPushPins + "' class='pushPin' src='/Content/Images/Maps/pushPinEnd.gif'><span class='pushPinText'>" + (noOfPushPins + 1) + "</span>");
} else {
routePointShape.SetCustomIcon("<img id='pushPin" + noOfPushPins + "' class='pushPin' src='/Content/Images/Maps/pushPinStart.gif'><span class='pushPinText'>" + (noOfPushPins + 1) + "</span>");
}
myRoutePoints.push(routePointShape);
map.Clear();
map.DeleteRoute();
map.AddShape(myRoutePoints);
});
}
There's an un-documented property called "Shape" on the Itinerary object. You can hide it...
More info here: http://social.msdn.microsoft.com/Forums/en/vemapcontroldev/thread/430449d0-fde4-4adb-9132-248fa6f9db65
Related
I am trying to save on localstorage the markers that I create on click in leaflet and delete them using a button. The problem is that I don't know how to delete just one marker at time and remove them from the main layer without affecting the others.
I want to add a unique id to each marker created (that's why the "Marker #") and later on delete see if the current marker's id or location (lat,lng) match the one stored on localstorage, then delete it from the localstorage and remove it from the main layer.
Anyone could help me solve this? This is giving me headaches!
I am using this function to add them on the map and in localstorage:
initUserLayerGroup();
map.on('click', onMapClick);
var groupUser;
function initUserLayerGroup() {
if (localStorage.userMarkers !== undefined) {
var storageMarkers = [];
var markersUser = [];
storageMarkers = JSON.parse(localStorage.userMarkers);
for (var i = 0; i < storageMarkers.length; i++) {
var x = storageMarkers[i].coords.x;
var y = storageMarkers[i].coords.y;
var name = storageMarkers[i].name;
var marker = L.marker([y, x]).bindPopup(name + "<br><a href='#' class='delete'>Delete</a>");
marker.on("popupopen", onPopupOpen);
markersUser.push(marker);
}
groupUser = L.layerGroup(markersUser);
map.addLayer(groupUser);
}
}
function onMapClick(e) {
var storageMarkers = [];
var markersUser = [];
if (localStorage.userMarkers !== undefined) {
storageMarkers = JSON.parse(localStorage.userMarkers);
}
storageMarkers.push({
"coords": {
"x": e.latlng.lng,
"y": e.latlng.lat
},
"name": "Marker #"
});
var x = storageMarkers[storageMarkers.length -1].coords.x;
var y = storageMarkers[storageMarkers.length -1].coords.y;
var name = storageMarkers[storageMarkers.length -1].name;
var marker = L.marker([y, x]).bindPopup(name + "<br>X: "+ x +", Y: "+ y +"<br><a href='#' class='delete'>Delete</a>");
marker.on("popupopen", onPopupOpen);
markersUser.push(marker);
groupUser = L.layerGroup(markersUser);
map.addLayer(groupUser);
localStorage.userMarkers = JSON.stringify(storageMarkers);
}
function onPopupOpen() {
var tempMarker = this.getLatLng();
$('.delete').click(function() {
localStorage.removeItem('userMarkers');
map.removeLayer(groupUser);
});
}
You can see it working here:
http://plnkr.co/edit/vYDExBBqy9zCGBRZJ944?p=preview
One way is to iterate over the saved array of coordinates in localStorage when the marker is clicked and compare them with the coordinates of the clicked marker. Once they are the same, delete this item from localSotrage and update it.
function onPopupOpen() {
var _this = this;
var clickedMarkerCoords = this.getLatLng();
$('.delete').click(function() {
storageMarkers = JSON.parse(localStorage.userMarkers);
for(i = storageMarkers.length; i > 0; i--) {
if (typeof storageMarkers[i] != 'undefined' &&
(clickedMarkerCoords.lat == storageMarkers[i].coords.y &&
clickedMarkerCoords.lng == storageMarkers[i].coords.x)
) {
storageMarkers.splice(i, 1);
localStorage.userMarkers = JSON.stringify(storageMarkers);
}
}
map.removeLayer(_this);
});
}
Here is the plunker: http://plnkr.co/edit/1xVZjKC1184dfuOlGqVX?p=preview
I tried the below code for that, but it adds pagedown buttons to only the first .wmd-input.
if ($(".wmd-input").length > 0) {
var converter = new Markdown.Converter();
var help = function () { alert("Do you need help?"); }
var options = {
helpButton: { handler: help },
strings: {quoteexample: "whatever you're quoting, put it right here"}
};
var editors = [];
var i = 0;
$(".wmd-input").each(function() {
editors[i] = new Markdown.Editor(converter, "", options);
editors[i].run();
i = i + 1;
});
}
Looks like i have to add unique ID for each element of wmd. I mean wmd-input, wmd-preview and wmd-button-bar. I modified this id attributes programmatically. This can be done with modifying manually but my length of inputs are dynamic.
// make wmd's id's unique
var pBox = $(this).parents(".box");
$(pBox).find("textarea").attr('id', "wmd-input" + i);
$(pBox).children("#wmd-preview").attr('id', "wmd-preview" + i);
$(pBox).find("#wmd-button-bar").attr('id', "wmd-button-bar" + i);
So when this ID attributes is set, i called the editor with postfix variable and problem solved.
editors[i] = new Markdown.Editor(converters[i], i, options);
if ($(".wmd-input").length > 0) {
var converters = [];
var editors = [];
var i = 1;
$(".wmd-input").each(function() {
converters[i] = new Markdown.Converter();
var help = function () { alert("Do you need help?"); }
var options = {
helpButton: { handler: help },
strings: {quoteexample: "whatever you're quoting, put it right here"}
};
// make wmd's id's unique
var pBox = $(this).parents(".box");
$(pBox).find("textarea").attr('id', "wmd-input" + i);
$(pBox).children("#wmd-preview").attr('id', "wmd-preview" + i);
$(pBox).find("#wmd-button-bar").attr('id', "wmd-button-bar" + i);
editors[i] = new Markdown.Editor(converters[i], i, options);
editors[i].run();
i = i + 1;
});
}
I am looking to query any attachments of layers based on the results of an Identify Task. If there are attachments on the layer, I would like to add the links to the bottom of the infoTemplate.
I am having trouble working the multiple dojo Deferred objects. I know they are being resolved because the return values are logged in the console, but my infoWindow is never populated.
So what is the proper way to deal with several nested deferreds? Deferred Lists seem to be a step in the right direction, but I am unsure how I would format one in this situation.
Thanks,
Joe
Update:
I have updated my working code below.
map.on('click',executeIdentify);
function executeIdentify(evt) {
identifyParams.width = map.width;
identifyParams.height = map.height;
identifyParams.geometry = evt.mapPoint;
identifyParams.mapExtent = map.extent;
var deferred = identifyTask.execute(identifyParams);
deferred.addCallback(function(deferredResult){
var promiseList = []
var features = array.map(deferredResult,function(result) {
var feature = result.feature;
var content = "";
array.forEach(Object.keys(feature.attributes),function(attr) {
content += attr + ": " + feature.attributes[attr] + "<br>"
});
var url = identifyTask.url + "/" + result.layerId + "/" + result.feature.attributes.OBJECTID + "/attachments?f=json"
var req = esriRequest({url:url}).then(function(newDef) {
if (Object.keys(newDef).toString() == "attachmentInfos,_ssl") {
content += "<br><b>Attachments:</b><hr>"
array.forEach(newDef.attachmentInfos,function(attach) {
content += "<a href=" + identifyTask.url + "/" + result.layerId + "/" + result.feature.attributes.OBJECTID + "/attachments/" + attach.id + " target='_blank'>" + attach.name + "</a><br>"
})
}
content += "<br><br>";
console.log(result)
feature.infoTemplate = new InfoTemplate(result.layerName + " " + result.feature.attributes.OBJECTID,content)
// console.log(feature)
return feature
},function(newDef) {
feature.infoTemplate = new InfoTemplate(result.layerName + " " + result.feature.attributes.OBJECTID,content);
// console.log(feature)
return feature
});
promiseList.push(req);
});
var promiseAll = new all(promiseList)
promiseAll.then(function(r) {promiseFun(r)})
})
function promiseFun(r) {
map.infoWindow.setFeatures(r);
map.infoWindow.show(evt.mapPoint);
}
}
You should look at using dojo/promise/all to handle multiple deferred results. Here's an example that uses it to return the results from multiple services, with some extra code to identify which service the result is coming from.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--The viewport meta tag is used to improve the presentation and behavior of the samples
on iOS devices-->
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<title>Identify with Popup</title>
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/esri/css/esri.css">
<style>
html, body, #map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
</style>
<script>var dojoConfig = { parseOnLoad: true };</script>
<script src="http://js.arcgis.com/3.8/"></script>
<script>
var map;
var identifyTask, identifyParams, idPoint;
var identifyResults;
require([
"esri/map", "esri/dijit/Popup", "dojo/promise/all", "dojo/domReady!"
], function (
Map, Popup, All
) {
var popup = new Popup({
fillSymbol: new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255, 0, 0]), 2), new dojo.Color([255, 255, 0, 0.25]))
}, dojo.create("div"));
map = new Map("map", {
basemap: "satellite",
center: [-83.275, 42.573],
zoom: 18,
infoWindow: popup
});
dojo.connect(map, "onLoad", mapReady);
var landBaseLayer = new esri.layers.ArcGISDynamicMapServiceLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/BloomfieldHillsMichigan/Parcels/MapServer", { opacity: .55 });
map.addLayer(landBaseLayer);
var militaryLayer = new esri.layers.ArcGISDynamicMapServiceLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Military/MapServer", { opacity: .55 });
map.addLayer(militaryLayer);
function mapReady(map) {
dojo.connect(map, "onClick", runIdentifies);
}
function runIdentifies(evt) {
identifyResults = [];
idPoint = evt.mapPoint;
var layers = dojo.map(map.layerIds, function (layerId) {
return map.getLayer(layerId);
});
layers = dojo.filter(layers, function (layer) {
if (layer.visibleLayers[0] !== -1) {
return layer.getImageUrl && layer.visible
}
}); //Only dynamic layers have the getImageUrl function. Filter so you only query visible dynamic layers
var tasks = dojo.map(layers, function (layer) {
return new esri.tasks.IdentifyTask(layer.url);
}); //map each visible dynamic layer to a new identify task, using the layer url
var defTasks = dojo.map(tasks, function (task) {
return new dojo.Deferred();
}); //map each identify task to a new dojo.Deferred
var params = createIdentifyParams(layers, evt);
var promises = [];
for (i = 0; i < tasks.length; i++) {
promises.push(tasks[i].execute(params[i])); //Execute each task
}
var allPromises = new All(promises);
allPromises.then(function (r) { showIdentifyResults(r, tasks); });
}
function showIdentifyResults(r, tasks) {
var results = [];
var taskUrls = [];
r = dojo.filter(r, function (result) {
return r[0];
});
for (i = 0; i < r.length; i++) {
results = results.concat(r[i]);
for (j = 0; j < r[i].length; j++) {
taskUrls = taskUrls.concat(tasks[i].url);
}
}
results = dojo.map(results, function (result, index) {
var feature = result.feature;
var layerName = result.layerName;
var serviceUrl = taskUrls[index];
feature.attributes.layerName = result.layerName;
var template = new esri.InfoTemplate("", "Service Url: " + serviceUrl + "<br/><br/>Layer name: " + result.layerName + "<br/><br/> Object Id: ${OBJECTID}");
feature.setInfoTemplate(template);
var resultGeometry = feature.geometry;
var resultType = resultGeometry.type;
return feature;
});
if (results.length === 0) {
map.infoWindow.clearFeatures();
} else {
map.infoWindow.setFeatures(results);
}
map.infoWindow.show(idPoint);
return results;
}
function createIdentifyParams(layers, evt) {
var identifyParamsList = [];
identifyParamsList.length = 0;
dojo.forEach(layers, function (layer) {
var idParams = new esri.tasks.IdentifyParameters();
idParams.width = map.width;
idParams.height = map.height;
idParams.geometry = evt.mapPoint;
idParams.mapExtent = map.extent;
idParams.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_VISIBLE;
var visLayers = layer.visibleLayers;
if (visLayers !== -1) {
var subLayers = [];
for (var i = 0; i < layer.layerInfos.length; i++) {
if (layer.layerInfos[i].subLayerIds == null)
subLayers.push(layer.layerInfos[i].id);
}
idParams.layerIds = subLayers;
} else {
idParams.layerIds = [];
}
idParams.tolerance = 3;
idParams.returnGeometry = true;
identifyParamsList.push(idParams);
});
return identifyParamsList;
}
});
</script>
</head>
<body>
<div id="map"></div>
</body>
</html>
This uses Raphaeljs to draw a single chord from an array:
function createChordStruct(key, string, shape) {
var string = string.toUpperCase();
var position = positions[string][key];
var struct = chord_shapes[shape];
return {
name: key + struct.name,
chord: struct.chord,
position: position,
position_text: struct.position_text,
bars: struct.bars
}
}
function createChordElement(chord_struct) {
var chordbox = $('<div>').addClass('chord');
var chordcanvas = $('<div>');
var chordname = $('<div>').addClass('chordname');
chordbox.append(chordcanvas);
chordbox.append(chordname);
chordname.append(chord_struct.name);
var paper = Raphael(chordcanvas[0], 150, 140);
var chord = new ChordBox(paper, 30, 30);
chord.setChord(
chord_struct.chord,
chord_struct.position,
chord_struct.bars,
chord_struct.position_text);
chord.draw();
return chordbox;
}
function createSectionElement(section_struct) {
var section = $('<div>').addClass('section');
var section_title = $('<div>').addClass('title');
var section_desc = $('<div>').addClass('description');
section.append(section_title);
section.append(section_desc);
section_title.append(section_struct.section);
section_desc.append(section_struct.description);
return section;
}
And this takes each chord created from the array and puts them in a new div called "chordscroller":
function c_i() {
var randomholder = 'id_' + (Math.floor(Math.random() * 100005) + 1);
var randomId = 'id_' + (Math.floor(Math.random() * 100005) + 1);
$(function () {
$('#sortable').append($('<li id ="' + randomholder + '" class="chordbox"><span id="i" class="scale">C - I</span><span id="' + randomId + '" class=" even scrollpane chordscroller"></span></li>').sortable( "refresh" ))
});
function c_one() {
var container = $("#" + randomId + "");
var column = null;
var column = _.shuffle(c_1);
for (var i = 0; i < column.length; ++i) {
var section_struct = column[i];
var section = createSectionElement(section_struct);
for (var j = 0; j < section_struct.chords.length; ++j) {
section.append(createChordElement(section_struct.chords[j]));
}
container.append(section);
}
}
$(function() { c_one() });
}
The problem is it draws all the chords at the same time and it takes forever. I've tried every combination of setTimeout and setInterval I could think of but I keep running into errors.
Can anybody tell from this code how to get the chords to be drawn one at a time instead of all at once?
Finally figured it out (using a plugin called doTimeout):
$.doTimeout( 1, function() {
chord.setChord(
chord_struct.chord,
chord_struct.position,
chord_struct.bars,
chord_struct.position_text);
chord.draw();
});
I found a code for city,state dropdown menu. It works flawlessly, but I am implementing additional feature by adding a US State to the following code:
//countries array
var countries = Object();
countries['Africa'] = '|Algeria|Angola|Benin';
//state array
var city_states = Object();
city_states['United States'] = '|Washington DC||Alabama|Alaska';
this is an array for US Cities, but I want to add a State abbreviation like so: DC, AL, AK and so on to be sent to the menu such as this:
function setRegions()
{
for (region in countries)
document.write('<option value="' + region + '">' + region + '</option>');
}
function set_country(oRegionSel, oCountrySel, oCity_StateSel)
{
var countryArr;
oCountrySel.length = 0;
oCity_StateSel.length = 0;
var region = oRegionSel.options[oRegionSel.selectedIndex].text;
if (countries[region])
{
oCountrySel.disabled = false;
oCity_StateSel.disabled = true;
oCountrySel.options[0] = new Option('SELECT COUNTRY','');
countryArr = countries[region].split('|');
for (var i = 0; i < countryArr.length; i++)
oCountrySel.options[i + 1] = new Option(countryArr[i], countryArr[i]);
document.getElementById('txtregion').innerHTML = region;
document.getElementById('txtplacename').innerHTML = '';
}
else oCountrySel.disabled = true;
}
function set_city_state(oCountrySel, oCity_StateSel)
{
var city_stateArr;
oCity_StateSel.length = 0;
var country = oCountrySel.options[oCountrySel.selectedIndex].text;
if (city_states[country])
{
oCity_StateSel.disabled = false;
oCity_StateSel.options[0] = new Option('SELECT NEAREST DIVISION','');
city_stateArr = city_states[country].split('|');
for (var i = 0; i < city_stateArr.length; i++)
oCity_StateSel.options[i+1] = new Option(city_stateArr[i],city_stateArr[i]);
document.getElementById('txtplacename').innerHTML = country;
}
else oCity_StateSel.disabled = true;
}
function print_city_state(oCountrySel, oCity_StateSel)
{
var country = oCountrySel.options[oCountrySel.selectedIndex].text;
var city_state = oCity_StateSel.options[oCity_StateSel.selectedIndex].text;
if (city_state && city_states[country].indexOf(city_state) != -1)
document.getElementById('txtplacename').innerHTML = city_state + ', ' + country;
else document.getElementById('txtplacename').innerHTML = country;
}
I was thinking adding an additional array of State abbreviations, but I think adding a simple state abbreviation to the already built array would do just fine by adding another value in the setregions() and having + abbreviation + instead of + region +. Any ideas how to implement it? -thank you.
If you have an array of States (objects) rather than an array of Strings you could do something like this:
function State(longName, shortName) {
this.longName = longName;
this.shortName = shortName;
}
I don't know what the abbreviations are, but store them like this in your array
var cityStates = "State:Abbrev|Washington DC:WDC|ETC:etc"
var stateNames = cityStates.split("|");
var states = new Array(stateNames.length);
for (i=0; i<states.length; i++)
var longName = stateNames[i].split(":")[0];
var shortName = stateNames[i].split(":")[1];
states[i] = new State(longName,shortName);
}
That would give you a new array "states" with 50 state objects, each which could be called like this:
states[0] //(returns a State object at index 0)
states[0].longName //(returns the long name)
states[0].shortName //(returns the abbreviated name)