using leaflet styled layer control and clusters - javascript

I'm creating a map that uses this styled layer control plug in
and the marker clusters plug in.
I've gotten both plug ins to work on their own with my data, but can't figure out how to get them to work together.
I have downloaded and included the marker cluster layer support files and attempted to implement them but it didn't change anything.
Basically there will be a category for each day of the week, and then within each day filters to show food or drink information, so I need this kind of layer control. I'm also open to suggestions for how to create my own layer control that is like this (grouping layers and then allowing you to filter within those groups)
var base = L.tileLayer('https://api.mapbox.com/styles/v1/agrosh/cj6p9fuxu2di72ss05n5nhycx/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYWdyb3NoIiwiYSI6ImNpeWFscjNkZzAwN3AycW55aXB6eWtjZnoifQ.ZudIxK3hMrxAX8O4BXhiEg', {
});
var zoomLevel = 13;
var setLat = 35.593464;
var setLong = -82.551934;
var map = L.map('map', {
center: [setLat, setLong],
zoom: zoomLevel
});
map.addLayer(base);
var mondayFood = [
{
"name":"name",
"details":"ex",
"address":"",
"website":"",
"lat":35.591140,
"lng":-82.552111,
"yelp":"",
"google":"",
"img":"img"
}];
var mondayDrink = [
{
"name":"name",
"details":"ex",
"address":"",
"website":"",
"lat":35.594446,
"lng":-82.555602,
"yelp":"",
"google":"",
"img":"img"
}];
var markerClusters = L.markerClusterGroup.layerSupport().addTo(map);
// monday
for ( var i = 0; i < mondayFood.length; ++i )
{
var monFood = mondayFood[i].img;
var mF = L.marker( [mondayFood[i].lat, mondayFood[i].lng], {icon: myIcon} )
.bindPopup( monFood );
markerClusters.addLayer( mF );
}
for ( var i = 0; i < mondayDrink.length; ++i )
{
var monDrink = mondayDrink[i].img;
var mD = L.marker( [mondayDrink[i].lat, mondayDrink[i].lng], {icon: myIcon} )
.bindPopup( monDrink );
markerClusters.addLayer( mD );
}
var overlays = [
{
groupName : "Monday",
expanded : true,
layers : {
"Food" : mondayFood
"Drink" : mondayDrink,
}];
}
var options = {
container_width : "300px",
group_maxHeight : "80px",
//container_maxHeight : "350px",
exclusive : false,
collapsed : true,
position: 'topright'
};
var control = L.Control.styledLayerControl(overlays, options);
map.addControl(control);

The Layers Control can handle Leaflet layers, not plain JavaScript data object.
In your case, you would probably not even directly use your mD and mF layers, since they are used only temporarily within your loops.
Instead, the classic way is to use Leaflet Layer Groups to gather your drink markers and food markers. MCG.layersupport is designed to be able to handle these Layer Groups.
var markerClusters = L.markerClusterGroup.layerSupport().addTo(map);
var groupFood = L.layerGroup().addTo(markerClusters);
var groupDrink = L.layerGroup().addTo(markerClusters);
for (var i = 0; i < mondayFood.length; ++i) {
var mF = L.marker([mondayFood[i].lat, mondayFood[i].lng]);
//markerClusters.addLayer(mF);
mF.addTo(groupFood);
}
for (var i = 0; i < mondayDrink.length; ++i) {
var mD = L.marker([mondayDrink[i].lat, mondayDrink[i].lng]);
//markerClusters.addLayer(mD);
mD.addTo(groupDrink);
}
var overlays = [{
groupName: "Monday",
expanded: true,
layers: {
//"Food": mondayFood,
//"Drink": mondayDrink
"Food": groupFood,
"Drink": groupDrink
}
}];
var control = L.Control.styledLayerControl(overlays);
map.addControl(control);
Live demo: http://plnkr.co/edit/ufoKZ0BJbjXILPV3iLpj?p=preview

Related

Dynamic google map icons

I have 2 tables one with markers another with marker icons. The icons are stored in the database as blob, where markers and icons are mapped by the ID's. The issue i am having is mapping the icons by id and markers type to the correct.
for example:
I'm trying to implement something like the following https://developers.google.com/maps/documentation/javascript/custom-markers
what i have done so far is the following
converting the overlay results(which i get via ajax and push to overlayRes object)
var binary = new Array();
for (i = 0; i < overlayRes.length; i++) {
var iconData = overlayRes[i].OverlayData.replace(/[^A-Fa-f0-9]/g, "");
for(var i=0; i< iconData.length / 2; i++){
var h = iconData.substr(i * 2, 2);
binary[i] = parseInt(h, 16);
}
}
byteArray = new Uint8Array(binary);
img = window.URL.createObjectURL(new Blob([byteArray], { type: 'application/octet-stream' }));
var icons = {
K: {
icon: img
},
L: {
icon: img
},
};
//markers
for (var i = 0; i < features.length; i++) {
var marker = new google.maps.Marker({
position: features[i].position,
icon: icons[features[i].type].icon, // need to get different markers per marker type
map: map
});
TIA

How to get all the pins/markers in on click of a markercluster in google maps?

I am using google maps api to create a store locator with clusters and I am referring the marker cluster api.
I wanted to get the list of stores with in a markercluster rather than returning marker cluster with pins/markers. Please find the below code -
google.maps.event.addListener(mapsCore.mapsVar.markerCluster, 'clusterclick', function(cluster) {
var content = "";
// Convert lat/long from cluster object to a usable MVCObject
var info = new google.maps.MVCObject;
info.set('position', cluster.center_);
//----
//Get markers
console.log(cluster.getSize());
var markers = cluster.getMarkers();
var x = {};
$(mapsCore.mapsVar.totalResults.Result).each(function(k, v) {
$(markers).each(function(km, vm) {
if (parseFloat(v.LAT) == parseFloat(markers[km].position.lat()) && parseFloat(v.LON) == parseFloat(markers[km].position.lng())) {
// locArr[k] = { lat: parseFloat(v.CounterLatitude), lng: parseFloat(v.CounterLongitude) };
x.Counter_ID = v.Counter_ID;
x.Counter_Name = v.Counter_Name;
x.Counter_Zip_code = v.Counter_Zip_code;
x.Address_1 = v.Address_1;
x.Address_2 = v.Address_2;
x.Province = v.Province;
x.City = v.City;
x.Area = v.Area;
x.SubArea = v.SubArea;
x.Counter_Tel = v.Counter_Tel;
x.Counter_Email = v.Counter_Email;
x.Open_Time = v.Open_Time;
x.Close_Time = v.Close_Time;
x.LAT = v.LAT;
x.LON = v.LON;
x.MR_FLG = v.MR_FLG;
mapsCore.mapsVar.clusterDetail.Results.push(x);
x = {};
}
});
});
});
As a workaround you can set a custom image to an transparent png and text size to 0, that way it'll be invisible on the map.
cluster.setStyles({
url: your_path/transparent.png,
height: 20,
width: 20,
textSize: 0
});
Alternatively you can try and see if setting the image height and width to 0 works.
All,
thanks for your help for formatting my code and comments any way I found the solution for it. I will attach the spinet of code below
google.maps.event.addListener(mapsCore.mapsVar.markerCluster, 'clusterclick', function(cluster) {
var content = '';
// Convert lat/long from cluster object to a usable MVCObject
var info = new google.maps.MVCObject;
info.set('position', cluster.center_);
//----
//Get markers
console.log(cluster.getSize());
var markers = cluster.getMarkers();
var x = {};
mapsCore.mapsVar.clusterDetail.Counters.length = 0;
$(mapsCore.mapsVar.totalResults.Counters).each(function(k, v) {
$(markers).each(function(km, vm) {
console.log(parseFloat(v.CounterLatitude) == parseFloat(vm.position.lat()) && parseFloat(v.CounterLongitude) == parseFloat(vm.position.lng()));
if (parseFloat(v.CounterLatitude) == parseFloat(vm.position.lat())) {
// locArr[k] = { lat: parseFloat(v.CounterLatitude), lng: parseFloat(v.CounterLongitude) };
x.CounterCode = v.CounterCode;
x.CounterName = v.CounterName;
x.CounterZipCode = v.CounterZipCode;
x.AddressLine1 = v.AddressLine1;
x.AddressLine2 = v.AddressLine2;
x.Province = v.Province;
x.City = v.City;
x.Area = v.Area;
x.SubArea = v.SubArea;
x.CounterPhoneNumber = v.CounterPhoneNumber;
x.CounterEmailAddress = v.CounterEmailAddress;
x.CounterOpenTime = v.CounterOpenTime;
x.CounterCloseTime = v.CounterCloseTime;
x.CounterLatitude = v.CounterLatitude;
x.CounterLongitude = v.CounterLongitude;
x.IsMagicRingAvailable = v.IsMagicRingAvailable;
mapsCore.mapsVar.clusterDetail.Counters.push(x);
x = {};
}
});
});
console.log(mapsCore.mapsVar.clusterDetail);
var template = $.templates("#mapslist");
var output = template.render(mapsCore.mapsVar.clusterDetail);
$(".store-list-section").html(output);
});
Always need to reset the array of object like -
mapsCore.mapsVar.clusterDetail.Counters.length = 0;

Removing GeoJson Layers from Google Maps

I'm trying to remove an array of GeoJson layers from Google Maps using the turf.js library for smoothing of the poly-lines.
I can create the layer fine and add them to the map, but when I try and remove the layers I get the following error code:
Uncaught TypeError: a.getId is not a function
To add the layers I do this, looping through my GeoJson file...
else if(Type==="LineString" && Pype==="isobar") {
//DO ISOBARS STUFF=================================
//GET LNG/LAT======================================
CorrLEN = dataID1.features[i].geometry.coordinates.length;
var Corrds =[];
var Corrs =[];
var LNGLAT ={};
var CORRS = new Object();
var x=0;
for (x=0;x<CorrLEN;x++){
var a = x-1;
LNGLAT = (dataID1.features[i].geometry.coordinates[x]);
Corrds.push(LNGLAT);
}
//=================================================================
//GET COLOUR INFO===================================================
var STCL = dataID1.features[i].properties.strokeColor;
var STOP = dataID1.features[i].properties.strokeOpacity;
var STSW = dataID1.features[i].properties.strokeWeight;
//=================================================================
LL = turf.linestring(Corrds);
curved = turf.bezier(LL,20000, 0.35);
curved.properties = {weight:STSW,color:STCL};
map.data.setStyle(function(feature) {
return {strokeWeight:feature.getProperty('weight'),
strokeColor: feature.getProperty('color')
};
});
IsoBars.push(curved);
I then use the following functions to add or remove the layers
//SHOW ISOBARS (works fine)
function ShowIsoBars() {
for (var i = 0; i < IsoBars.length; i++) {
map.data.addGeoJson(IsoBars[i]);
}}
//HIDE ISOBARS (Gets the error) Uncaught TypeError: a.getId is not a function
function HideIsoBars() {
for (var i = 0; i < IsoBars.length; i++) {
map.data.remove(IsoBars[i]);
}}
Many thanks in advance,
I found a solution by taking the new smoothed coordinates and then using them in a new google.maps.Polyline like so
var Path =[];
var x=0;
for (x=0;x<CorrLEN;x++){
Path.push(new google.maps.LatLng(curved.geometry.coordinates[x][1],curved.geometry.coordinates[x][0]));
}
var IsoBar = new google.maps.Polyline({
path: Path,
geodesic: true,
strokeColor: STCL,
strokeOpacity: STOP,
strokeWeight: STSW
});
IsoBars.push(IsoBar);
And then I can use the the following functions to show or hide the layers
function ShowIsoBars() {
for (var i = 0; i < IsoBars.length; i++) {
IsoBars[i].setMap(map);
}}
function HideIsoBars() {
for (var i = 0; i < IsoBars.length; i++) {
IsoBars[i].setMap(null);
}}
I found rather than removing all objects from the layer. It was easier to destroy and recreate the layer, which circumvents the error:
//Remove layer from map if it exists
if (mapLayer != null) {
mapLayer.setMap(null);
}
//create new layer
mapLayer = new window.google.maps.Data({ map: googleMap });

Changing z-order of layer in Leaflet

I created an example of layers using as base this code:
http://bl.ocks.org/ragnarheidar/a711faa1a94be4dae48f
The piece of code responsible to creating the layers is the following:
function getColor(d)
{
return marker_colors[d];
}
function marker_style(i)
{
return {
fillColor: getColor(i),
radius: 5,
weight: 1,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
};
}
//data URL variables
var start_date = '2013-08-01'; //YYYY-MM-DD
var end_date = '2013-08-08'; //YYYY-MM-DD
var c_type = 'Noise'; // Complaint Type
// Build the data URL
var URL = "http://data.cityofnewyork.us/resource/erm2-nwe9.json";
URL += "?";
URL += "$where=";
URL += "(latitude IS NOT NULL)";
URL += " AND ";
URL += "(complaint_type='" + c_type + "')";
URL += " AND ";
URL += "(created_date>='" + start_date + "') AND (created_date<='" + end_date + "')";
URL += "&$group=complaint_type,descriptor,latitude,longitude";
URL += "&$select=descriptor,latitude,longitude,complaint_type";
URL = encodeURI(URL);
console.log(URL);
var noise_description = ["Air Condition/Ventilation Equipment",
"Alarms",
"Banging/Pounding",
"Barking Dog",
"Car/Truck Horn",
"Car/Truck Music",
"Construction Equipment",
"Construction Before/After Hours",
"Engine Idling",
"Ice Cream Truck",
"Jack Hammering",
"Lawn Care Equipment",
"Loud Music/Party",
"Loud Talking",
"Loud Television",
"Manufacturing Noise",
"Private Carting Noise",
"Others"];
var marker_colors = ['#7f3b08',
'#a50026',
'#d73027',
'#f46d43',
'#fdae61',
'#fee090',
'#ffffbf',
'#ffffff',
'#e0f3f8',
'#abd9e9',
'#74add1',
'#4575b4',
'#313695',
'#d8daeb',
'#b2abd2',
'#8073ac',
'#542788',
'#2d004b'];
// Load GeoJSON from an external file
$.getJSON(URL, function(data)
{
var markers = []
var layers = []
for (var i = 0; i < noise_description.length; i++)
{
markers[i] = [];
}
var all_markers = [];
$.each(data, function(index, rec)
{
var marker;
for (var i = 0; i < noise_description.length; i++)
{
if (rec.descriptor.indexOf(noise_description[i]) > -1)
{
marker = L.circleMarker([rec.latitude, rec.longitude], marker_style(i));
markers[i].push(marker);
all_markers.push(marker);
break;
}
}
});
// Create layer of all markers but do not add to map
var all_layers = L.featureGroup(all_markers);
// Create specific layers of markers and add to map
for (var i = 0; i < markers.length; i++)
{
layers[i] = L.featureGroup(markers[i]).addTo(map);;
}
map.fitBounds(all_layers.getBounds());
// Create object containing all marker layers
var overlays = {};
for (var i = 0; i < noise_description.length; i++)
{
overlays[noise_description[i]] = layers[i];
}
//add layer control using above object
L.control.layers(null,overlays).addTo(map);
});
That's the last part of my code, but it's being showed behind another layer (this behavior changes with some refreshment of the page):
I'm wondering how to control this.
All vectors in Leaflet 0.x (I guess you are using Leaflet v0.7.7 from your previous question) are added to a single SVG container in the overlayPane.
This includes your first areas (I think you did not include that code in the current question?) as well as your Circle Markers.
Their stack order depend on their insertion order. So because you load them asynchronously, depending on the time it takes for the server to send the data, your Circle Markers may be below or above your areas.
You could either chain your 2 asynchronous requests to predefine in which order they execute. You would simply start the next query once the first one has completed. But you would delay the data display.
Or you could force some vectors to be brought on top / bottom using bringToFront / bringToBack methods. You would have to loop through your feature group and apply it on each individual layer, like:
all_layers.eachLayer(function (layer) {
layer.bringToFront();
});
The situation is different in Leaflet 1.0, where you can create your own panes and specify their z-index (possibly through CSS). Then you can insert your layers (including vectors) in any desired pane.

Google Map One Infobox Open

I am building a directory, in this example it's a directory for doctors. I created a javascript array called "locations". The visitor can check checkboxes on the map to choose which kind of doctor should be displayed.
This is a sample of the array of locations to loop through in a for loop
var locations = [
[0, 'Total Care', 1, 0, 0, 0, 0, 0, 'Lake Elsinore', '92530', 'CA', 33.6603, -117.3830, '(951) 674-8779', 1],
... etc
];
This explains each key
locations[i][0] = business claimed or not (0 = unclaimed and 1 is claimed)
locations[i][1] = name
locations[i][2] = if general practitioner = 1, else = 0
locations[i][3] = if surgeon = 1, else = 0
locations[i][4] = if cardiologist = 1, else = 0
locations[i][5] = if urologist = 1, else = 0
locations[i][6] = if gynecologist = 1, else = 0
locations[i][7] = if pulmonologist = 1, else = 0
locations[i][8] = city
locations[i][9] = zip code
locations[i][10] = state
locations[i][11] = latitude
locations[i][12] = longitude
locations[i][13] = phone number
locations[i][14] = z-index
All works fine. I have a search function so the visitor can search by name. In the google map code below, I want to find a way to have the infoBox open on the marker of the doctor that was entered in the search function e.g.
if (locations[i][1] == "doctor name"){
code here }
I have been trying to find a solution for the past three days and can't find it, so I would really appreciate some help. This is the Google Map code:
var infoBox = null;
function initialize()
{
var centerMap = new google.maps.LatLng(33.6603, -117.3830);
var mapOptions = {zoom: 11,center: centerMap,mapTypeId: google.maps.MapTypeId.ROADMAP}
var map = new google.maps.Map(document.getElementById('googleMap'), mapOptions);
setMarkers(map, locations);
}
function setMarkers(map, markers)
{
var image = {url: 'images/marker.png',size: new google.maps.Size(17, 23),origin: new google.maps.Point(0,0),anchor: new google.maps.Point(8, 23)};
gpr = $('#check1').is(':checked') ? 1 : 0; // general practitioner
srg = $('#check2').is(':checked') ? 1 : 0; // surgeon
car = $('#check3').is(':checked') ? 1 : 0; // cardiologist
uro = $('#check4').is(':checked') ? 1 : 0; // urologist
gyn = $('#check5').is(':checked') ? 1 : 0; // gynecologist
pul = $('#check6').is(':checked') ? 1 : 0; // pulmonologist
for (var i = 0; i < markers.length; i ++)
{
var locations = markers[i];
var siteLatLng = new google.maps.LatLng(locations[11], locations[12]);
var boxText = document.createElement('div');
boxText.style.cssText = 'some styling';
link = locations[1].replace(' ','_');
link = link.toLowerCase();
// find out if this genre of doctor was searched for
setMarker = 0;
if (gpr == 1){if (locations[2] == 1){setMarker = 1;}}
if (srg == 1){if (locations[3] == 1){setMarker = 1;}}
if (car == 1){if (locations[4] == 1){setMarker = 1;}}
if (uro == 1){if (locations[5] == 1){setMarker = 1;}}
if (gyn == 1){if (locations[6] == 1){setMarker = 1;}}
if (pul == 1){if (locations[7] == 1){setMarker = 1;}}
// if one of the checkboxes was checked
if (setMarker == 1)
{
if (locations[0])
{
boxText.innerHTML = 'some html with link'; // claimed business
}
else
{
boxText.innerHTML = 'some html without link'; // unclaimed business
}
var infoBoxOptions = {content: boxText,disableAutoPan: false,maxWidth: 0,pixelOffset: new google.maps.Size(5, -80),zIndex: locations[14],boxStyle: {background: "url('images/tip.png') no-repeat",opacity: 0.9,width: "405px",height: "75px",border: '0px solid #900'},closeBoxMargin: "13px 5px 5px 5px",closeBoxURL: "images/close.gif",infoBoxClearance: new google.maps.Size(1, 1),isHidden: false,pane: "floatPane",enableEventPropagation: false};
var marker = new google.maps.Marker({position: siteLatLng,map: map,title: locations[1],zIndex: locations[14],icon: image,html: boxText});
google.maps.event.addListener(marker, 'click', function (e) {infoBox.setContent(this.html);infoBox.open(map, this);});
var infoBox = new InfoBox(infoBoxOptions);
}
}
}
Your help will be much appreciated. Thank you.
Unless I'm misunderstanding, you can just loop through your markers until the title matches the search text, and then open the appropriate info box on that marker. Something like:
$('#searchButton').click(function() {
var searchText = $('#searchBox').val();
for (var i = 0; i < markers.length; i++) {
if (markers[i].title.indexOf(searchText) > -1)
infoBoxes[i].open(map, markers[i]);
}
});
This example assumes you have two existing parallel arrays of markers and their corresponding info boxes.

Categories

Resources