Highlight custom icons in Leaflet - javascript

I'm using markers with custom images in Leaflet, like this:
// A template for icons when they get instantiated on the map
var UnitIcon = L.Icon.extend({
options: {
iconSize: [40, 40],
iconAnchor: [20, 35]
}
});
function PlaceIconOnMapAtLatLng(iconURL, lat, lng)
{
var newIcon = new UnitIcon({iconUrl: iconURL});
var myMarker = L.marker([lat, lng], {icon: newIcon, draggable: true}).addTo(map);
}
The custom images sit in a folder and get read at run-time. The idea is that the user can change these and have as many as they want.
My problem is that when it comes to highlighting these, e.g. on click, there doesn't seem to be any straightforward way to do this in Leaflet. Initially I thought about just drawing a shape around the icon, but then this would be treated as its own separate thing that would be dragged around separately, whereas I want it to stay with its associated icon at all times.
A horrible first attempt would be something like having an update running constantly that sets the position of the highlight to whatever the selected marker's position is.
Or is there some way to associate objects e.g. as "children" so that when their parent object moves, the child moves with it?
I would preferably like an explicit highlight instead of doing something like changing the size or opacity of the selected marker, or giving it a pop-up, although these could be fall-back options. The reason I want a highlight is because ultimately I want to be able to highlight multiple icons at once, and having loads of pop-ups etc. doesn't seem like a very nice way of doing that.

You can add a CSS-Class to the Icon.
marker.on('click', function (e){
var layer = e.target;
if(!L.DomUtil.hasClass(layer._icon, 'dash-border')){
L.DomUtil.addClass(layer._icon,'dash-border');
}else{
L.DomUtil.removeClass(layer._icon,'dash-border');
}
});
.dash-border {
border: 2px dashed #3388ff;
background-color: #3388ff4d;
}
https://jsfiddle.net/falkedesign/r9onevq2/

Related

leaflet layer interrupting interactivity of base layer

I am working on a project based on the Leaflet Choropleth Tutorial.
In my map, I have three base layers that the user switches between
var basemaps= {
"layer1": layer1,
"layer2": layer 2,
"layer3": layer 3
};
Each of these layers highlights and displays popup information just like in the Choropleth template.
I also have layers that display labels(values) for each region, depending which one is active.
var activelabel = L.layerGroup();
var showlabels = {
"labels": activelabel
};
map.on('baselayerchange', function (event) {
activelabel.clearLayers();
switch (event.layer) {
case layer1:
label1.addTo(activelabel);
break;
case layer2:
label2.addTo(activelabel);
break;
case layer3:
label3.addTo(activelabel);
break;
}
});
My problem, is that when the labels are active, if the user hovers over the part of the region where the label is, the highlight feature and info box do not display any information.
I am trying to find a way to make the activelabel layer invisible, so that the hover still works.
I found the tutorial on Map Panes, which seems like the solution to my problem, but it doesn't seem to do anything.
I'm not sure if i'm putting the pane information in the wrong spot, or if this doesn't actually work like I think it should? I've tried changing the xIndex everywhere from 0 to 700 and it doesn't seem to make a difference?
map.createPane('labels');
map.getPane('labels').style.zIndex = 700;
map.getPane('labels').style.pointerevents = 'none';
I've tried putting the pane: 'labels' code here, where I format my labels
var createLabelIcon = function(labelClass,labelText){
return L.divIcon({
className: labelClass,
html: labelText
})
};
as well as here, where I create the layer group.
var label1 = new L.layerGroup();
makelabel1();
Any help would be appreciated. Thank you.
I figured out a solution to this. I have three separate .js files where I add all the layers containing labels to my layergroups.
If I add {interactive: false} to each instance of .addLayer in each file, the labels to not interrupt the mouse hover.
I'm not sure why this doesnt work when I add interactive:false to the layer group as opposed to each addLayer line, but it seems to be working.

Google Maps API v3, Remove Marker icon or changing to "null"?

I'm writing a Python Flask application in which I'm using Google Maps. I want to be able to add labels to a polyline that I've drawn which symbolizes a ship route.
The route is drawn using a set of coordinates and the polyline feature of the Maps API. I want to add time labels to the polyline and the easiest way seems to be to use Map Markers. However I don't want the large standard pins to show up, but would prefer a small icon/marker together with my text or even none at all. As far as I have gathered you can create "Circles" (which are modifiable) or "Markers" (which you only can change the icon of). I would prefer to go with "Circles", but those you apparently can't add text to..
How can I add text to my map and avoid the Google Maps Pins showing up?
Currently I have an list of objects that contains latitude, longitude and date + time. I'm iterating through it adding markers, but as I do I would like to keep out the marker icon or instead draw the circles if someone knows how to draw circles with added text?
for(i = 0; i < markerList.length; i++){
var position = new google.maps.LatLng(markerList[i].lat, markerList[i].lng);
var date = markerList[i].date;
var marker = new google.maps.Marker({
position: position,
label: date,
map: map,
icon: "None" //Produces error: 404 (NOT FOUND)
});
}
Being able to change the label size also is a very much appreciated function, but I have been unable to find any information about whether that is available. Being able to change the color of the text would also be nice.
As no answers have been given yet and I've sort of found a solution to my problem I guess I will share for others out there with the same problem. At least until someone comes up with a better solution:
I ended up using a predefined symbol and scaling it down to 0 in size as follows:
for(i = 0; i < markerList.length; i++){
var position = new google.maps.LatLng(markerList[i].lat, markerList[i].lng);
var date = markerList[i].date;
var marker = new google.maps.Marker({
position: position,
label: date,
map: map,
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 0
}
});
}
Sadly I haven't found a way to mess with the label yet.
try markerWithlabel and you can change the icon of the marker whit a svg or png plus the label too
like this jsfiddel
.
#Zeliax You can add visible: false to not have marker icon show on your google map. icon prop looks for a path that you want specify for your marker to look as. It is basically a url for your display image.

d3.js Shape scaling/google map controls and showing markers inside a shape area

Working on this application which I have broken down here.
http://jsfiddle.net/pPMqQ/81/
In this example I want to
show markers only inside the shape area
allow for zoom of the map and scaling of the shape area
here is my pseudo code
identifyMarkersInShape: function(){
//__ function is invoked every time a shape is drawn/editted
// hides all the markers
//finds the markers inside the given shape
},
bindEvents: function(){
//handle zoom of the map and scale of the path shape
}
Fully working example: http://jsfiddle.net/PDf9G/5/
Edit: Now simplifies polygon before adding it to the map. Editing the polygon works as well.
First, the css and html: I moved the div called #canvas1 after the map and gave it absolute positioning and z-index = 0. I also gave the map the same absolute positioning to ensure that they always line up with each other, and gave it a z-index of 10.
When the draw button is clicked the canvas is moved to the front. The user can use it to draw free form using d3. When they are done the shape they drew is converted to a google maps polygon.
addShapeToBaseMap: function(divCoords) {
var geoCoords = []
for (var i = 0; i < divCoords.length; i++)
geoCoords.push(overlay.getProjection().fromContainerPixelToLatLng(new google.maps.Point(Number(divCoords[i][0]), Number(divCoords[i][1]))));
poly = new google.maps.Polygon({
paths: geoCoords,
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35
});
poly.setMap(map);
Then we do do the hiding/showing of the markers. Using d3 this way is really silly and defeats the purpose. You need to add the google geometry library to your url to use this (&libraries=geometry). I'm sure there are faster ways and if you're dealing with large datasets you'll want to make this better.
for (var i = 0; i < data.length; i++) {
if (!google.maps.geometry.poly.containsLocation(new google.maps.LatLng(data[i]['lat'], data[i]['lng']), poly)) {
d3.select("#" + data[i]['name']).classed({'hide': true});
} else {
d3.select("#" + data[i]['name']).classed({'hide': false});
}
}
This works because when we appended the markers we added their name as the id on the marker element. The only reason I can see to do this is because the svg gives you better control over styling. Last:
svg.select(".selection").remove()
d3.select("#canvas1").classed({'front': false});
$('.draw').removeClass('highlight');
},
We remove the shape we drew from the drawing layer. If we don't do this, if the user moves the map and then turns the drawing layer back on, the shape will be in the wrong place. Then we move the canvas to the back and turn off the highlighting on the drawing button.
The edit function was taken from your most recent code. If the edit button or polygon is clicked the editing function is turned on on the polygon.
I would also recommend taking a look at Leaflet. The integration with d3 is a bit easier and you can have multiple svg layers, which would allow you to put the drawing layer as a map overlay instead of a separate div.
geojson-utils is a node/browser javascript package that has a bunch of utilities for dealing with geojson paths.
One of the many things that it has is a very solid point in polygon algorithm designed for dealing with geojson paths.
You also might want to consider using Leaflet.js instead of Google Maps, it has a few more tools for going to and from geojson based data. It also has a lot of really useful tools already written like Leaflet.draw which has the code already needed to "draw" these paths on top of the maps.

How to find lat/lon location that is closest to mouse cursor on the google map

I have many markers.
On the left I have list of marker-abstractions of actual markers on the google map.
I decided to use polylines
var flightPlanCoordinates = [
new google.maps.LatLng(near_cursor_lat, near_cursor_lon),
new google.maps.LatLng(marker_lat, marker_lon)
];
var flightPath = new google.maps.Polyline({
path: flightPlanCoordinates,
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2
});
flightPath.setMap(map);
the idea is that user points his mouse on the marker-abstraction box on the left, and then he shown the line which goes from marker-abstraction to actual marker on the map. I already have everything except of the STARTING POINT(red dots on the image)
How do I find starting lat/long which is located next to the mouse pointer, when the mouse is pointed at the box on the left
Some details:
When the mouse is pointed to the box that sais PERSON1, I want the coordinates of the first red dot. When the mouse is pointed to the box that sais PERSON2, I want the coordinates of the second red dot. And so on. There is a trick part- left boxes are located outside the google maps div; in addition, if there are many boxes with PERSONS, the left div will allow to scroll those persons up and down, so the vertical correlation between the persons box, and the red dot is dynamic.
In theory, I think, I need an event that is triggered when I point to one of the boxes. When even is fired, I need to measure the vertical distance in pixels from the top to the mouse pointer. Then, when I have the vertical distance, I need to perform some action that would measure same vertical distance on the google map, and would get me that point on the map in lat/lon coordinates.
This should be the answer to your question :
You should be using markers to represent (persons) and then add a listener onMouseOver like in the below post :
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map); // 'map' is new google.maps.Map(...)
Use overlay in the listener to get the projection and the pixel coordinates:
google.maps.event.addListener(marker, 'mouseover', function() {
var projection = overlay.getProjection();
var pixel = projection.fromLatLngToContainerPixel(marker.getPosition());
// use pixel.x, pixel.y ... (after some rounding)
});
copied from :
Get Position of Mouse Cursor on Mouseover of Google Maps V3 API Marker
#MehdiKaramosly answer is pretty spot on.
Only slightly furthering the above, to get what would be the lat lng of an element that is not part of the visible map (if the map was visible there) you can pass the events page X/Y to the line:
projection.fromLatLngToContainerPixel(marker.getPosition());
like so:
var pixelLatLng = overlay.getProjection().fromContainerPixelToLatLng(new google.maps.Point(e.pageX,e.pageY));
I have put a very basic example at: http://tinkerbin.com/p30yEXqH
If you click anywhere outside of the map (which equates to the same as clicking on a div that overlays it), you will see in the console log that the lat/lng for wherever you have clicked (even though it is not in the visible map space), is logged.
Cheers,
C.
Update with Working Example of this code
Second Update with JsFiddle Example of both ways click and open map window or mouseover map to highlight listing
As i understand the problem you are having is as following:
Persons are listed in the div separate from map
Persons have latitude and longitude associated to them on the map
Map displays location of person with markers on it using lat long (might be stored in db or something)
You want it so people can highlight a person on map as well as list with mouse over.
If you have lat/long available on map or from list you need to relate them together.
Solution is something like this but there are many ways to achieve this mapping
In your div where person is listed. Insert a data-mapid attribute to each person element when a person hovers over it you highlight it and get the data-mapid from there.
On your map when you render a marker you can additionally pass a parameter data-mapid or something else with same value and have a highlight function on map as well.
`jQuery("#gmap3ul li[data-gb='plist']").each(function(){
elemtopush = {
lat:jQuery(this).attr("data-lat"),
lng:jQuery(this).attr("data-long"),
data:{
"ht":jQuery(this).html(),
"id":jQuery(this).attr("data-mapid")
}
};
ulmarkerspgb.push(elemtopush);
});`
In above code i have html to show on map as well as id as data-mapid now this mapid can be person1 person2 and so on so you can relate back to div with lists. i am using ul and li to list them in the div.
On mouse over your markers events you can do something like this
mouseover: function(marker, event, data)
{
clearalllist();
var listelement = jQuery("td[data-mapid='"+data.id+"']");
jQuery(listelement).attr('style','background-color:#ccc');
var map = jQuery(this).gmap3('get'),
infowindow = jQuery(this).gmap3({action:'get', name:'infowindow'});
if (infowindow){
infowindow.open(map, marker);
infowindow.setContent(data.ht);
google.maps.event.addListener(infowindow, 'closeclick', function(event) {
jQuery(listelement).attr('style','');
});
google.maps.event.addListener(infowindow, 'close', function(event) {
jQuery(listelement).attr('style','');
});
} else {
jQuery(this).gmap3({action:'addinfowindow', anchor:marker, options:{content: data.ht}});
infowindow = jQuery(this).gmap3({action:'get', name:'infowindow'});
google.maps.event.addListener(infowindow, 'closeclick', function(event) {
jQuery(listelement).attr('style','');
});
google.maps.event.addListener(infowindow, 'close', function(event) {
jQuery(listelement).attr('style','');
});
}
}
There is a lot of redundant code i have copied pasted from 3 locations so you might just be able to get what you want out of it.
This can make both map linked to list and list linked to map. See my second update on top or click this JSFiddle example. When you click on list on top it opens the map window and when you mouse over the map icons it highlights the listing.
This also populates the map via list rather than hard coding lat longs in js.

Change Icon of Single Vector Feature in Layer

Currently I am trying to change the icon of a particular feature of a vector layer that the user is focusing on. I add each feature to the map like so:
var point = new OpenLayers.Geometry.Point(pt.lon, pt.lat);
var markerStyle = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {
externalGraphic: iconURL
});
var marker = new OpenLayers.Feature.Vector(point, attributes, markerStyle);
Later I do the following to update the feature's icon:
var marker = this.findSelectedMarker();
if (marker) {
marker.style.externalGraphic = newIconUrl;
this.layer.redraw();
}
But when the layer redraws, all features in my layer use the newIconUrl, not simply the selected marker that I am trying to update.
How can I change the icon of the one selected feature of my layer? Thanks.
There were two issues I needed to fix in order to solve this. The first was related to using multiple OpenLayers styles, both at the layer level as well as the individual feature level. I removed the styling for each individual feature, so that only the following layer style was being implemented:
this.layerStyle = new OpenLayers.StyleMap({
'default': {
externalGraphic: media_url + '${iconURL}',
graphicHeight: 32,
graphicWidth: 32,
graphicXOffset: -16,
graphicYOffset: -32,
fillOpacity: 0.75
}
});
The second change I made was to use attribute replacement syntax to designate the icon URL with a feature attribute called '${iconURL}'. This allowed me to change the icon url by simply changing an attribute of the selected feature and redraw the layer:
focusedMarker.attributes.iconURL = this.focusedURL;
this.layer.redraw();

Categories

Resources