How do I access and hide markers created by loadGeoJson()? - javascript

I am loading custom coordinates into my map application via JSON. I have been able to find out how to color code the markers based on feature properties, but one of my next steps will be to create filters to show or hide markers based on the properties.
My code starts like this:
var map;
var infowindow = new google.maps.InfoWindow();
function initialize()
{
var mapCanvas = document.getElementById('map');
var mapOptions = {
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(mapCanvas, mapOptions);
map.data.loadGeoJson('/map_json.php', null, SetBounds);
map.data.setStyle(function(feature) {
var color = 'FF0000';
var symbol = '%E2%80%A2'; // dot
// color selection code here [...]
return /** #type {google.maps.Data.StyleOptions} */ {
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=' + symbol + '|' + color
};
}
I already found how I can access the imported data through a jquery autocomplete search:
$(input).autocomplete({
minLength: 0,
source: function(request, response) {
data = [];
map.data.forEach(function(feature)
{
var str = request.term.toUpperCase();
if (String(feature.getProperty('name')).toUpperCase().indexOf(str) > -1)
{
data.push({id: feature.getProperty('id'), name: feature.getProperty('name'), address: feature.getProperty('address')});
}
});
response(data);
},
select: function(event, ui)
{
map.data.forEach(function(feature)
{
if (feature.getProperty('id') == ui.item.id)
{
var content = GetContent(feature);
infowindow.setContent(content);
infowindow.setPosition(feature.getGeometry().get());
infowindow.setOptions({pixelOffset: new google.maps.Size(0, -34)});
infowindow.open(map);
// zoom in
map.setZoom(15);
map.panTo(feature.getGeometry().get());
return false;
}
});
}
})
.autocomplete().data('uiAutocomplete')._renderItem = function(ul, item)
{
return $('<li>')
.append('<a>' + item.name + ' (ID: ' + item.id + ')<br>' + item.address + '</a>')
.appendTo(ul)
};
So using this same principle to run my filters is not a problem.
The problem is that I have not found a way yet to access the visible markers based on the feature information that I have in map.data.
All the examples I found so far are based on the principle of manually adding markers and storing them in an array to access later, e.g.:
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
title: 'Hello World!'
});
But I don't have that - I load the entire set of data using getGeoJson().
How can I access the marker and manipulate it (e.g. hide it or show it) based on the information I can access using map.data.forEach()?
--- Update ---
Here are more details on the project.
The map will markers that are generated from a list of customers. The customers have different categories and properties, so a typical entry form the GeoJSON string would look like this:
{"type":"Feature","geometry":{"type":"Point","coordinates":[0,0]},"properties":{"name":"Customer 1","id":"1001","address":"1234 Test Street, Test City, TX 12345, USA","category":"vendor","active":1}}
Also on the map is a filter box with checkboxes that are checked by default. Clicking any of them will run the filtering code that should hide or remove the markers that are associated with any customers that match that filter.
So if I disable the checkbox that filters "inactive", then only customers with the property "active":1 will remain on the map. If I disable the checkbox that filters "vendors", then all customers with the category "vendor" will be hidden.
Checking the checkboxes again later will undo the hiding of these entries.
What I have found in my research is a lot of mentioning of markers, but ONLY if they are added manually - not via GeoJSON import.
I can see a few potential solutions to my problem - I could ignore the GeoJSON format and instead import the client list into jQuery manually and parse it from there into markers that then go into an array. But then why use the GeoJSON format at all?
My current solution of using map.data.setStyle() (see comment) seems to work and do the job. But I am curious if there isn't another more direct way.
I figured, the filter function would go through all data (map.data.forEach()) to locate any items that should be hidden based on the filters, and then each item would communicate to its associated marker that the marker needs to be hidden. But it is this association that I have not been able to figure out so far.
When I loop through all features (map.data.forEach()), I have access to the data I uploaded, but not to the markers that were placed as a result of the import.
My question is if there is a direct way to access the marker from the feature.
I hope this is clearer now.
--- Update ---
I created a very simple jsfiddle for it:
http://jsfiddle.net/semmelbroesel/9bv68ngp/
This is the concept I want to achieve, and it works as is. My only question is if there is another way to achieve the same results by directly accessing the placed markers instead of using setStyle() to hide/show them.

You don't need to use forEach, since setStyle does already traverse the Features.
If you declare the styling function as:
map.data.setStyle(function(feature) {
var color = 'FF0000';
var symbol = '%E2%80%A2'; // dot
return /** #type {google.maps.Data.StyleOptions} */ {
visible: feature.getProperty('active'), // this links visibility to feature property
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=' + symbol + '|' + color
};
});
You don't need to call the method again, since the style gets bound to the feature property. Setting the active property to false will propagate to the marker style seamlessly.
After that, you can make your filters like (rough example)
var setFilter = function(property, value) {
map.data.forEach(function(feature) {
feature.setProperty('active', feature.getProperty(property) === value);
});
};
and calling for example setFilter('name','John');
Again, this is a rough example. I'd rather implement the filtering method on the google.maps.Data prototype, but this should point you in the right direction.

Related

How to customize HERE Maps Clusters

In my current Hybrid app I am in the process of switching from using Leaflet and its Clusterer plugin to using the HERE Maps v3 JavaScript API. The HERE Maps documentation can be rather dense at times for someone used to the clarity of Leaflet documentation. Nevertheless the process of using HERE is fairly straightforward. However, there is one Leaflet feature that I really miss:
In Leaflet when you add markers to a cluster it is possible to assign custom map pins to each marker. Further, you can quite easily customize the pin used to represent the cluster itself. In HERE the documentation indicates the following
var dataPoints = [];
//create an array for the clustered marker datapoints
dataPoints.push(new H.clustering.DataPoint(43.25539364396839, -79.07150530321474));
dataPoints.push(new H.clustering.DataPoint(43.255434408174246, -79.07175552759227));
dataPoints.push(new H.clustering.DataPoint(43.25557588373579, -79.07203209137799));
dataPoints.push(new H.clustering.DataPoint(43.25567419706804, -79.07218354297491));
//populate that array
var clusteredDataProvider = new H.clustering.Provider(dataPoints);
//create a cluster data provider and assign it the freshly created data points
var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
//create a new layer that uses this provider
map.addLayer(layer);
//inject this layer into the map
This works. However, it leaves me with three unanswered questions
How can I make the Cluster icon "explode" when tapped so the map zooms in automatically to the clustered map pins and shows each individual map pin with its assigned icon?
How do I assign individual PNG icon images to each of the "datapoints" above?
How to I customize the look and feel of the actual Cluster map pin itself?
Clusters and noise points are represented on the map with markers. Unless otherwise configured, the clustering provider uses the default bitmap markers theme with weight information to display clusters and noise points on the map. You can define your own custom theme and pass it to the provider as the theme property.
Read please this docu on https://developer.here.com/documentation/maps/3.1.22.0/dev_guide/topics/clustering.html
A custom theme is defined in an object that implements the interface H.clustering.ITheme as demonstrated by the code below.
/**
* Make clustering of markers with a custom theme
*
* Note that the maps clustering module https://js.api.here.com/v3/3.1/mapsjs-clustering.js
* must be loaded to use the Clustering
*
* #param {H.Map} map A HERE Map instance within the application
* #param {H.ui.UI} ui Default ui component
* #param {Function} getBubbleContent Function returning detailed information about photo
* #param {Object[]} data Raw data containing information about each photo
*/
function startClustering(map, ui, getBubbleContent, data) {
// First we need to create an array of DataPoint objects for the ClusterProvider
var dataPoints = data.map(function(item) {
// Note that we pass "null" as value for the "altitude"
// Last argument is a reference to the original data to associate with our DataPoint
// We will need it later on when handling events on the clusters/noise points for showing
// details of that point
return new H.clustering.DataPoint(item.latitude, item.longitude, null, item);
});
// Create a clustering provider with a custom theme
var clusteredDataProvider = new H.clustering.Provider(dataPoints, {
clusteringOptions: {
// Maximum radius of the neighborhood
eps: 64,
// minimum weight of points required to form a cluster
minWeight: 3
},
theme: CUSTOM_THEME
});
// Note that we attach the event listener to the cluster provider, and not to
// the individual markers
clusteredDataProvider.addEventListener('tap', onMarkerClick);
// Create a layer that will consume objects from our clustering provider
var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
// To make objects from clustering provider visible,
// we need to add our layer to the map
map.addLayer(layer);
}
// Custom clustering theme description object.
// Object should implement H.clustering.ITheme interface
var CUSTOM_THEME = {
getClusterPresentation: function(cluster) {
// Get random DataPoint from our cluster
var randomDataPoint = getRandomDataPoint(cluster),
// Get a reference to data object that DataPoint holds
data = randomDataPoint.getData();
// Create a marker from a random point in the cluster
var clusterMarker = new H.map.Marker(cluster.getPosition(), {
icon: new H.map.Icon(data.thumbnail, {
size: {w: 50, h: 50},
anchor: {x: 25, y: 25}
}),
// Set min/max zoom with values from the cluster,
// otherwise clusters will be shown at all zoom levels:
min: cluster.getMinZoom(),
max: cluster.getMaxZoom()
});
// Link data from the random point from the cluster to the marker,
// to make it accessible inside onMarkerClick
clusterMarker.setData(data);
return clusterMarker;
},
getNoisePresentation: function (noisePoint) {
// Get a reference to data object our noise points
var data = noisePoint.getData(),
// Create a marker for the noisePoint
noiseMarker = new H.map.Marker(noisePoint.getPosition(), {
// Use min zoom from a noise point
// to show it correctly at certain zoom levels:
min: noisePoint.getMinZoom(),
icon: new H.map.Icon(data.thumbnail, {
size: {w: 20, h: 20},
anchor: {x: 10, y: 10}
})
});
// Link a data from the point to the marker
// to make it accessible inside onMarkerClick
noiseMarker.setData(data);
return noiseMarker;
}
};
/**
* Boilerplate map initialization code starts below:
*/
// Helper function for getting a random point from a cluster object
function getRandomDataPoint(cluster) {
var dataPoints = [];
// Iterate through all points which fall into the cluster and store references to them
cluster.forEachDataPoint(dataPoints.push.bind(dataPoints));
// Randomly pick an index from [0, dataPoints.length) range
// Note how we use bitwise OR ("|") operator for that instead of Math.floor
return dataPoints[Math.random() * dataPoints.length | 0];
}
/**
* CLICK/TAP event handler for our markers. That marker can represent either a single photo or
* a cluster (group of photos)
* #param {H.mapevents.Event} e The event object
*/
function onMarkerClick(e) {
// Get position of the "clicked" marker
var position = e.target.getGeometry(),
// Get the data associated with that marker
data = e.target.getData(),
// Merge default template with the data and get HTML
bubbleContent = getBubbleContent(data),
bubble = onMarkerClick.bubble;
// For all markers create only one bubble, if not created yet
if (!bubble) {
bubble = new H.ui.InfoBubble(position, {
content: bubbleContent
});
ui.addBubble(bubble);
// Cache the bubble object
onMarkerClick.bubble = bubble;
} else {
// Reuse existing bubble object
bubble.setPosition(position);
bubble.setContent(bubbleContent);
bubble.open();
}
// Move map's center to a clicked marker
map.setCenter(position, true);
}
// Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
// Step 2: initialize a map
var map = new H.Map(document.getElementById('map'), defaultLayers.vector.normal.map, {
center: new H.geo.Point(50.426467222414374, 6.3054632497803595),
zoom: 6,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
// Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Step 4: create the default UI component, for displaying bubbles
var ui = H.ui.UI.createDefault(map, defaultLayers);
/**
* Merges given data with default bubble template and returns resulting HTML string
* #param {Object} data Data holding single picture information
*/
function getBubbleContent(data) {
return [
'<div class="bubble">',
'<a class="bubble-image" ',
'style="background-image: url(', data.fullurl, ')" ',
'href="', data.url, '" target="_blank">',
'</a>',
'<span>',
// Author info may be missing
data.author ? ['Photo by: ', '<a href="//commons.wikimedia.org/wiki/User:',
encodeURIComponent(data.author), '" target="_blank">',
data.author, '</a>'].join(''):'',
'<hr/>',
'<a class="bubble-footer" href="//commons.wikimedia.org/" target="_blank">',
'<img class="bubble-logo" src="data/wikimedia-logo.png" width="20" height="20" />',
'<span class="bubble-desc">',
'Photos provided by Wikimedia Commons are <br/>under the copyright of their owners.',
'</span>',
'</a>',
'</span>',
'</div>'
].join('');
}
// Step 5: request data that will be visualized on a map
startClustering(map, ui, getBubbleContent, photos);
This worked example try please on JsFiddle: https://jsfiddle.net/gh/get/jquery/2.1.0/heremaps/maps-api-for-javascript-examples/tree/master/custom-cluster-theme
The data for this example loaded from https://heremaps.github.io/maps-api-for-javascript-examples/custom-cluster-theme/data/photos.js
How the Cluster icon to "explode" when tapped you can see sources of this example https://tcs.ext.here.com/examples/v3.1/cluster_marker_spider . Note please on loaded additional js module "ClusterMarkerSpider-..."
How zoom-in to "exploded" of the clustered map pins - note please on "forEachDataPoint" (in Spider example) there are created all Markers which you can simple push to some H.map.Group and after zoom-in to this group by its bounding box.

How do i get the name or identification of a marker JVectorMap

How do I know the name or id of the marker that is clicked?
And is it possible?
Is there a way to find the label/name of the marker like this for example:
onMarkerClick: function(label){
alert(label.text());
}
All help appreciated :)
The second parameter in the click handler function is the key/index of the marker object.
onMarkerClick (Event e, String code)
One possible way to do that, is:
onMarkerClick: function(e, code) {
var mapObj = $("#map").vectorMap("get", "mapObject");
var idx = parseInt(code); // optional
var name = mapObj.markers[idx].config.name;
var latitude = mapObj.markers[idx].config.latLng[0];
var longitude = mapObj.markers[idx].config.latLng[1];
}
Explanation:
depending how you created the markers, you may need to get the index
as numeric value
by default, each marker is storing the name and the coords of the point
If you need more info, try to explore the properties of the map object in the browser, like this: console.log(mapObj.markers);

Custom Map Tile Overlay Issues

I have a need to display a couple hundred to perhaps one thousand high resolution aerial photographs over the standard satellite imagery provided by Google maps. The images are geographically dispersed, so I decided to implement a tile server as a generic asp.net handler (*.ashx file). I will be basing my issue descriptions on the map shown on Google's developer site at the following URL:
https://developers.google.com/maps/documentation/javascript/examples/maptype-overlay
Everything is more-or-less working, but I am having the following two issues:
1) After selecting the "Satellite" map type, hovering over that button produces a dropdown with a checkbox called "Labels". How can I add another checkbox to that dropdown titled "Aerial Photographs" that will toggle my overlay on/off? Will I have to hard-code JQuery hacks that utilize Google Maps implementation details, or can I accomplish this through the API?
2) My *.ashx handler returns either the image, or a status 204 (no content) if the specified tile does not exist. The issue is that the 204 results are not cached, so every time I zoom out and back in to the same location, my server gets re-hit for all the tiles that the client should already know don't exist. I failed to see it documented what a tile server should return for such an "empty" tile, so the client can cache the result. What should I return if there is no map tile for a specific location?
Thanks.
Given the lack of response to this question, it is clear that sparse tile servers are an uncommon practice. The following is the solution (however hacky it may be) for both problems:
1) How do I add a checkbox to the "Satellite" dropdown to toggle my map layer? Unfortunately, there is no supported way to do this, so I came up with the following INCREDIBLY hacky code:
// Create a function to select the "Labels" checkbox
var getLabelButton = function() {
return $('.gm-style-mtc:last > div:last > div:last');
};
// Clone the "Labels" checkbox used to show/hide the hybrid map overlay
var labelBtn = getLabelButton();
var labelClone = labelBtn.clone();
// Change the display and hover text for the new button
labelClone.prop('title', Localizer.GetString('CustomImagery.Description'));
labelClone.find('label').html(Localizer.GetString('CustomImagery.Name'));
// Highlight the button when the client hovers the mouse over it
var checkbox = labelClone.children('span');
var prevBackColor = labelClone.css('background-color');
var prevBorderColor = checkbox .css('border-color');
labelClone.hover(function() {
labelClone.css('background-color', '#EBEBEB');
checkbox .css('border-color' , '#666');
}, function() {
labelClone.css('background-color', prevBackColor);
checkbox .css('border-color' , prevBorderColor);
});
// Set the checkmark image source to be the correct value, instead of transparent
var checkmark = checkbox .children('div');
var checkmarkImg = checkmark.children('img');
checkmarkImg.attr('src', 'https://maps.gstatic.com/mapfiles/mv/imgs8.png');
// Attach the new checkbox after the Labels checkbox
labelBtn.after(labelClone);
// Create a method to determine if the selected map type supports custom imagery
var mapTypesSupportingCustomImagery = [
google.maps.MapTypeId.SATELLITE,
google.maps.MapTypeId.HYBRID
];
var isImagerySupportedOnSelectedMapType = function() {
var mapTypeId = googleMap.getMapTypeId();
return (0 <= mapTypesSupportingCustomImagery.indexOf(mapTypeId));
};
// Show the checkmark and imagery if the initial map type supports it
if (isImagerySupportedOnSelectedMapType()) {
checkmark.css('display', '');
googleMap.overlayMapTypes.push(tileServer);
}
// Show/hide the checkmark and imagery when the user clicks on the checkbox
labelClone.on('click', function() {
var showImagery = (checkmark.css('display') === 'none');
if (showImagery) {
checkmark.css('display', '');
googleMap.overlayMapTypes.push(tileServer);
} else {
checkmark.css('display', 'none');
var tileServerIndex = googleMap.overlayMapTypes.indexOf(tileServer);
googleMap.overlayMapTypes.removeAt(tileServerIndex);
}
});
// Create a function that returns whether the custom imagery should be displayed
var displayCustomImagery = function() {
return (isImagerySupportedOnSelectedMapType() && checkmark.css('display') != 'none');
};
// Add an event listener to add the tile server when displaying satellite view
google.maps.event.addListener(googleMap, 'maptypeid_changed', function() {
var tileServerIndex = googleMap.overlayMapTypes.indexOf(tileServer);
if (displayCustomImagery()) {
if (tileServerIndex < 0) {
googleMap.overlayMapTypes.push(tileServer);
}
} else if (0 <= tileServerIndex) {
googleMap.overlayMapTypes.removeAt(tileServerIndex);
}
});
In the above code, googleMap is the map object, and tileServer is my implementation of a google.maps.ImageMapType object.
2) What should I return to represent an empty tile?
My solution to this question was quite a bit cleaner. I simply list the file names of all the tiles on the server, which are a base-4 encoding of the Morton number for the tile being requested. Then I send this list to the client as a dictionary from string to bool (always true). The client simply checks to see if the server contains a map tile before making the request, so the server doesn't have to worry about what to return (I left it as returning a 204 error if an invalid request is made). The javascript to get the tile name within the getTileUrl method is as follows:
function(coord, zoom) {
// Return null if the zoom level is not supported
// NOTE: This should not be necessary, but minZoom and
// maxZoom parameters are ignored for image map types
if (zoom < minZoom || maxZoom < zoom) {
return null;
}
// Get the name of the map tile being requested
var tileName = '';
var y = coord.y << 1;
for (var shift = zoom - 1; 0 <= shift; --shift) {
var digit = (coord.x >>> shift) & 1;
digit |= ( y >>> shift) & 2;
tileName += digit;
}
// Return if the map tile being requested does not exist
if (!mapTiles[tileName]) {
return null;
}
// Return the url to the tile server
...
}

Assign ID to marker in leaflet

So i try to achieve a result as on foursquare: https://foursquare.com/explore?cat=drinks&mode=url&near=Paris which is when you clik on a marker on the map, it scrolls through the listed of restaurants on the right -hand side of the screen to the ad hoc restaurant, and highlights it through CSS. Conversely, when you click on the restaurant on the list, it highlights it on the map.
I am using skobbler/leaflet. I think I can achieve this by amending dynamically CSS as shown in this example: http://jsfiddle.net/gU4sw/7/ + a scroll to destination script already in place in the page.
To achieve this however, it looks like I need to assign an ID within the markers (2 markers below):
var marker = L.marker([52.52112, 13.40554]).addTo(map);
marker.bindPopup("Hello world!<br>I am a popup1.", { offset: new L.Point(-1, -41) }).openPopup();
var marker = L.marker([52.53552, 13.41994]).addTo(map);
marker.bindPopup("Hello world!<br>I am a popup2.", { offset: new L.Point(-1, -41) }).openPopup();
Question is: How can I assign an marker ID to trigger css change in the corresponding element within my html page?
My knowledge of JS is very limited, but may be there's a nice and easy solution out there, thx
I've been looking for a nice way to do this and as far as I can tell there is still no built-in way (using leaflet) to give a marker an ID. I know I'm a bit late to answering this but hopefully it will help others who stumble upon this question. As far as I can tell there are two main issues here:
Problem #1:
Unless you save your markers to an object or map, described below, there is no easy programmatic way of accessing them later on. For example - A user clicks something OUTSIDE the map that corresponds to a marker INSIDE the map.
Problem #2:
When a user clicks on a marker INSIDE the map, there is no built in way to retrieve the ID of that marker and then use it to highlight a corresponding element or trigger an action OUTSIDE the map.
Solutions
Using a one or more of these options will help address the issues described above. I'll start with the one mentioned in the previous answer. Here is the working pen, which holds all the code found below.
Option #1:
Save each marker, using a hardcoded or dynamic ID, inside an object -
// Create or retrieve the data
var data = [
{
name: 'Bob',
latLng: [41.028, 28.975],
id: '2342fc7'
}, {...}, {...}
];
// Add an object to save markers
var markers = {};
// Loop through the data
for (var i = 0; i < data.length; i++) {
var person = data[i];
// Create and save a reference to each marker
markers[person.id] = L.marker(person.latLng, {
...
}).addTo(map);
}
Similar to the other answer you can now access a single marker by using -
var marker = markers.2342fc7; // or markers['2342fc7']
Option #2:
While leaflet doesn't provide a built-in 'id' option for markers, you can add the an ID to the element directly by accessing ._icon property:
// Create and save a reference to each marker
markers[person.id] = L.marker(person.latLng, {...}).addTo(map);
// Add the ID
markers[person.id]._icon.id = person.id;
Now when you handle click events, it's easy as pie to get that marker's ID:
$('.leaflet-marker-icon').on('click', function(e) {
// Use the event to find the clicked element
var el = $(e.srcElement || e.target),
id = el.attr('id');
alert('Here is the markers ID: ' + id + '. Use it as you wish.')
});
Option #3:
Another approach would be use the layerGroup interface. It provides a method, getLayer, that sounds like it would be perfect get our markers using an ID. However, at this time, Leaflet does not provide any way to specify a custom ID or name. This issue on Github discusses how this should be done. However you can get and save the auto-generated ID of any Marker (or iLayer for that matter) like so:
var group = L.layerGroup()
people.forEach(person => {
// ... create marker
group.addLayer( marker );
person.marker_id = group.getLayerId(marker)
})
Now that we have every marker's ID saved with each backing object in our array of data we can easily get the marker later on like so:
group.getLayer(person.marker_id)
See this pen for a full example...
Option #4:
The cleanest way to do this, if you have the time, would be to extend the leaflet's marker class to handle your individual needs cleanly. You could either add an id to the options or insert custom HTML into the marker that has your id/class. See the documentation for more info on this.
You can also you use the circleMarker which, in the path options, you will see has an option for className which can be nice for styling groups of similar markers.
Styling:
Almost forgot that your original question was posed for the purpose of styling... simply use the ID to access individual elements:
.leaflet-marker-icon#2342fc7 { ... }
Conclusion
I'll also mention that layer and feature groups provide another great way to interface with markers. Here is a question that discusses this a bit. Feel free to tinker with, or fork either the first or second pen and comment if I've missed something.
An easy way to do this is to add all the markers to a list with a unique id.
var markersObject = {};
markersObject["id1"] = marker1;
markersObject["id2"] = marker2;
markersObject["id3"] = marker3;
If the list of restaurants have a property in the html element of a single restaurant that corresponds to the id of the added marker. Something like:
Click
Then add the click event where you will pass the id of the restaurant (in this case "data-restaurantID") and do something like:
markersObject["passedValueFromTheClickedElement"].openPopup();
This way once you click on the item in the list a markers popup will open indicating where on the map is the restaurant located.
var MarkerIcon = L.Icon.extend({
options: {
customId: "",
shadowUrl: 'leaf-shadow.png',
iconSize: [64, 64],
shadowSize: [50, 64],
iconAnchor: [22, 94],
shadowAnchor: [4, 62],
popupAnchor: [-3, -76]
}
});
var greenIcon = new MarkerIcon({iconUrl: "/resources/images/marker-green.png"}),
redIcon = new MarkerIcon({iconUrl: "/resources/images/marker-red.png"}),
orangeIcon = new MarkerIcon({iconUrl: "/resources/images/marker-orange.png"});
var mymap = L.map('mapid').setView([55.7522200, 37.6155600], 13);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
id: 'mapbox.streets'
}).addTo(mymap);
// добавить маркер
L.marker([55.7522200, 37.6155600], {customId:"010000006148", icon: greenIcon, title:setMarkerTitle("010000006148")}).addTo(mymap).on('click', markerOnClick);
L.marker([55.7622200, 37.6155600], {customId:"010053166625", icon: redIcon, title: setMarkerTitle("010053166625")}).addTo(mymap).on('click', markerOnClick);
function markerOnClick(e) {
var customId = this.options.customId;
document.location.href = "/view/abonents/" + customId;
}
function setMarkerTitle(customId){
var result = customId;
result += "\nline2 ";
result += "\nline3 ";
return result;
}
For my case, I found the best way was to generate and pass a unique ID to L.marker's Options object when I create it.
const newMarker = L.marker([lat, lng], { uniqueID })
You can then add this marker to a leaflet layerGroup.
const newLayerGroup = L.layerGroup().addTo(map);
newLayerGroup.addLayer(newMarker);
You can access the ID with layer.options.uniqueID This allows me to find and manipulate the marker later; all I need is Leaflet's .eachLayer() and the uniqueID.
My backend (Cloud Firestore) already generates unique document ID's, which makes it super easy to sync my Leaflet map and backend in real-time, rather than rebuilding and remounting the entire layerGroup or refreshing the page.
//e.g. a callback which fires whenever a doc has been removed from my db
newLayerGroup.eachLayer((layer) => {
if (deletedDocID === layer.options.uniqueID) {
newLayerGroup.removeLayer(layer);
}
});
I just added an ID to the extended control that got created, by using setAttribute.
Here is an example:
var someViewer = L.Control.extend({
onAdd: function () {
var someContainer = L.DomUtil.create('div');
var someDiv = L.DomUtil.create('div');
someDiv.setAttribute('id', 'SPQR_Gauge'); // <-- Just add this line to get an ID
// code continues down ...
After that, you can use pretty much anything. Style, innerHTML, you name it.
document.getElementbyId('someDiv').style = // bla bla bla
My solution is storing ID in e.target.options
function mark_click(e){
console.log(`${e.target.options.id} has been click`);
}
for (var i in data) {
var row = data[i];
var marker = L.marker([row.lat, row.lng], {
opacity: 1,
icon: myIcon,
id:123
});
marker.addTo(mymap).on('click', mark_click);
}
Leaflet's className option can allow one to add identifiers to objects:
var onMouseover = function() {
// returns all members of the specified class
d3.selectAll(".mySpecialClass")
.style("opacity", ".1");
};
// add desired class to pointer
L.circleMarker([46.85, 2.35], {className: "mySpecialClass"})
.addTo(map).on('mouseover', onMouseover);
// to select the marker(s) with a particular class, just use css selectors
// here's a d3.js solution
d3.selectAll(".mySpecialClass")
.style("opacity", ".3")
A fairly straight forward and easy way to accomplish creating an array of clickable markers within a leaflet map object is to manipulate the class list of the created marker by adding a custom incremented class name to each marker. Then it is easy to create a listener and know which marker was clicked. By skipping the active one or not, each has a retrievable click event with a reliable ID.
// creates markers, each with a leaflet supplied class
if (length === 1) {
for (i = 0; i < parks.length; ++i) {
if (parks[i].parksNumber !== parks.parksNumber)
L.marker([parks[i].latitude, parks[i].longitude], {
icon: parks[i].iconMarker
}).addTo(mymap);
}
}
// select all of the leaflet supplied class
let markers = document.querySelectorAll(".leaflet-marker-icon");
// loop through those elements and first assign the indexed custom class
for (i = 0; i < markers.length; ++i) {
markers[i].classList.add("marker_" + parks[i].parksNumber);
// then add a click listener to each one
markers[i].addEventListener("click", e => {
// pull the class list
let id = String(e.target.classList);
// pull your unique ID from the list, be careful cause this list could
// change orientation, if so loop through and find it
let parksNumber = id.split(" ");
parksNumber = parksNumber[parksNumber.length - 1].replace("marker_", "");
// you have your unique identifier to then do what you want with
search_Number_input.value = parksNumber;
HandleSearch();
});
}
1.) Lets create Marker with unique id...
L.marker([marker.lat, marker.lng],{customID:'some ID',title:marker.title}).on('click', this.markerClick).addTo(mymap);
2.) Go to node_modules#types\leaflet\index.d.ts and add customID?:string;
export interface MarkerOptions extends InteractiveLayerOptions {
icon?: Icon | DivIcon;
title?: string;
....
autoPanSpeed?: number;
customID:string;
}
3.) In the same file add customID to LeafletMouseEvent
export interface LeafletMouseEvent extends LeafletEvent {
latlng: LatLng;
layerPoint: Point;
containerPoint: Point;
originalEvent: MouseEvent;
customID:customID
}
4.) Create customID class
export class customID {
constructor(customID: string);
customID: number;
}
5.) Get your marker id in function
markerClick(e){
console.log(e.sourceTarget.options.customID)
}

Integrate Google Map with Markers from HTTPrequest Javascript

Hi there I am Using appcelerator, and I want to integrate a map with an array of markers I am getting from a HTTPRequest...
I am effing lost, totally lost.
This is how the map looks like:
var mapview = Titanium.Map.createView({
mapType: Titanium.Map.STANDARD_TYPE,
region: {latitude:33.74511, longitude:-84.38993,
latitudeDelta:0.01, longitudeDelta:0.01},
animate:true,
regionFit:true,
userLocation:true,
annotations:[mountainView]
});
And I have the example of 1 marker hardcoded ...
var mountainView = Titanium.Map.createAnnotation({
latitude:37.390749,
longitude:-122.081651,
title:"Appcelerator Headquarters",
subtitle:'Mountain View, CA',
pincolor:Titanium.Map.ANNOTATION_RED,
animate:true,
leftButton: '../images/appcelerator_small.png',
myid:1 // CUSTOM ATTRIBUTE THAT IS PASSED INTO EVENT OBJECTS
});
So yo create the marker and in the annotations section you add it to the map, the thing here is that I am getting the markers from this:
var url = "http://myURLwithMyParameters";
var xhr = Ti.Network.createHTTPClient({
onload: function(e) {
// this function is called when data is returned from the server and available for use
// this.responseText holds the raw text return of the message (used for text/JSON)
var result = this.responseText;
var xml = Ti.XML.parseString(result);
var items = xml.documentElement.getElementsByTagName("marker");
var name = xml.documentElement.getElementsByTagName("name");
var value = xml.documentElement.getElementsByTagName("address");
var data = [];
for (var i=0;i<items.length;i++) {
data.push({
name: items.item[i].getElementsByTagName("name")[0].textContent,
address: items.item[i].getElementsByTagName("address")[0].textContent
})
Does any one know how to integrate this?
I think I must build the map in the same function as the markers, but I've tried several options and haven't found ANY example of this in the web.
Any clue would be very appreciated.
Thank you in advance.
If all you have is an address, you'll need to forward geocode those addresses to get lat/long coordinates. Those coords are required to place annotations on the map. Check the docs at forwardGeocoder(). There's an example in the KitchenSink

Categories