I'm working on a map that uses leaflet and is populated by data from a file in a GeoJson format. My overarching goal is to put graphs into the leaflet popups for each marker on the map.
Getting the markers for each feature and getting the popups to open was fairly easy. However, I am finding it difficult to use D3 to add to the popup.
For the sake of simplicity my goal at the moment is to use D3 to create a svg within each leaflet popup div and draw a square.
I have found some examples where people have used D3 to create graphs inside leaflet popups, but none of them were also using geoJson and the onEachFeature function. This is one of the examples:http://jsfiddle.net/6UJQ4/
Here is the relevant part of my code:
L.geoJson( data, {
style: function (feature) {
return { opacity: 0, fillOpacity: 0.5, fillColor: "#0f0" };
},
onEachFeature: function(feature, layer){
var graph_container = '<div class="popupGraph" id="graph" style="width: 200px; height:200px;"></div>';
layer.bindPopup(feature.properties.name + '<br>' + graph_container);
var svg = d3.select("#graph").selectAll("svg").append("svg").attr("width", 50).attr("height", 200);
var rectangle = svg.append("rect").attr("width", 25).attr("height", 25);
}
}).addTo(map);
I believe I am having issues because D3 can't find the graph_container div however I am a little stumped on how to fix this.
If anyone has any experience using D3, Leaflet, and geoJson together and could explain to me how to get my square to show in the popups or if anyone knows of a source that could help me. I would appreciate it a lot.
Thanks in advance!
UPDATE: Bits has solved my problem! If anyone needs a working example of using D3 in Leaflet popups in combination with GeoJson, Bits posted it in the comments but I will post it here aswell: http://jsfiddle.net/2XfVc/132/
Its quite simple, you just need to add and svg element inside of your div. And start coding d3.
Give me a moment, I am updating your fiddle.
Update: Here you go http://jsfiddle.net/6UJQ4/6/
I took the liberty of simplifying/stripping your example to lowest common denominator to reduce confusion.
Update: http://jsfiddle.net/6UJQ4/7/
In the previous fiddle, you will come across issues where all your markers will be selected everytime giving undesired results. So use last update.
Related
I set up a PostGIS database that I added in GeoServer via a parameterized SQL view. I used Leaflet to display this layer via wms.
It worked fine until I add GeoWebCache using the url "/geoserver/gwc/service/wms" instead of "/geoserver/wms". I can still see my polygons when I'm at the minimal zoom. But then when I zoom I see only a red polygon and a half of a green polygon and if I zoom again I see only the red polygon. You can see these 3 states on the images below:
I guess this is a problem of tiling: I get the minimal tiles and also some tiles around the red polygon for further zooms but for some reason it seems that the other tiles are not sent.
Here is the code I use to get my wms layer with leaflet:
geoJSONlayer = L.tileLayer.wms("/geoserver/gwc/service/wms", {
layers: 'cartowiki:choix',
format: 'image/png',
transparent: true,
viewparams: 'year:'+(annee+3000)
}).addTo(map);
geoJSONlayer.addTo(map);
Do you have an idea of the problem here ?
Thanks in advance,
The bounding box was the problem indeed. In Geoserver, I had to modify the properties of the layer in 2 places :
I clicked 'Compute from SRS bounds' and then 'Compute from native bounds' in the Bounding Boxes part of the Data section
I erased and created again the available gridsets in the Tile Caching section so that the grid subset bounds would update with the new Bounding Boxes
I hope it can help someone in the future!
I'm using Mapbox JS with Leaflet. I've been trying to find a way to rotate a polygon by dragging a corner or something similar, and I found Leaflet.Path.Transform that does exactly what I need, like this example (mainly the rotating and dragging part).
I looked at their example, and tried to use that for my particular situation. But I haven't been able to figure out how to access the script file required for that. Their GitHub page tells me to include the dist/L.Path.Transform.js file. So I went to this page and copied the code and pasted it in the script.js file in Plunker, and linked to it in the code. This is what I have so far in Plunker.
For now, I'm just trying to add a new polygon and try to rotate/drag that.
var polygon = new L.Polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
], {
color: '#f00',
transform: true
}).addTo(map);
//polygon.transform.enable({rotation: true, scaling: false});
But it is giving me some JavaScript errors. What am I doing wrong here? Or if there is some other simple demo that I can refer to, that'll be great as well.
Thanks to help from the person who developed Leaflet.Path.Transform (w8r), I was about to figure out what I needed. There is a sample demo in this link if anyone needs it. It is possible to rotate/drag the given polygon. In the original question, the issue was that I couldn't figure out how to link to the required script. But this is what I had missed first:
<script src='https://unpkg.com/leaflet-draw-drag#0.1.7/dist/Leaflet.draw.drag.js'></script>
<script src="https://unpkg.com/leaflet-path-transform#0.0.6/dist/L.Path.Transform.js"></script>
Now it works as I intended.
I have been working on creating a map that uses google maps API and Fusion Tables together to create a county overlay over North Dakota as shown below,
(How The map Should look)
The counties are drawn from KML data in a fusion table I have created. The issue is that 90% of the time when you first load the page or when you change one of the parameters in the 'Map Options' Menu that updates the layer, the map renders like one of these following photos. (The dots are a separate layer from a separate fusion table and they work flawlessly. it is the county layer that is having issues)
(Problems)
I know that the data is being loaded because you can still click on the layer where it is not being loaded properly and the functions all still work, it seems that it is just not rendering the img tiles properly. It also loads properly and displays everything properly about 20% of the time, so it seems like my code to shade the polygons is working. It looks like they are loaded in many tiles 256x256 and some of them are bugged pretty badly...(the below photo shows the tile being loaded properly, other times there are parts within the 256x256img missing)
-I can't post more than two photos yet so if you want to see this please let me know.-
It seems like when I add one more constraint to the query to just display one county it works every time. So I am led to believe that it is just having a hard time drawing all the counties at once.
Below Is how I am updating the layer when a new year is selected, or a new species is selected.
function updateLayerBySpecies(map,layer,locationLayer)
{
if(species!='Human_Cases' && species !='Bird_Cases')
{
var colors=COLORS; //COLORS is an array of 5 colors
var minNum= 0;
var maxNum=50;
var step= (maxNum-minNum) / colors.length;
var styles = new Array();
for(var i=0;i<colors.length;i++)
{
var newMin = minNum + step *i;
styles.push({where:generateWhereYS(newMin,year,species),
polygonOptions: {fillColor:colors[i],
fillOpacity: 1,
strokeColor: '#000000',
strokeWeight: 3}});
}
layer.set('styles',styles);
layer.setMap(map);
if(document.getElementById('showLoc').checked==false)
locationLayer.setMap(null);
else
{
//alert("should be setting the layer next");
locationLayer.setMap(map);
}
}
I don't know if this is a problem with the way I am querying the table or if it is a google side problem. If anyone has any information about this issue or any solutions please let me know. I have tried changing the way the layer options are set and have tried a few other methods but to no avail. This is really disappointing because if it would load properly every time this would be a fantastic way of visualizing the data I have. PLEASE let me know if you have any advice or ideas!
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.
I want to split my map into tiles/territories. So i've prepared another layer showing squares. But this layer is full of .png image files so there is no data/object for this squares.
I've also tried to draw squares with leaflet's geometry objects. But it causing performance issues, there is times to show 500+ squares.
If you develop something like that what method would you prefer? UTFGrid? GeoJSON/Geometry? Or maybe any other better solution?
UPDATE:
Actually i don't want to get data belongs to square's territory i just want to change the square's color somehow i mean somehow i want to highlight that area maybe i can create a rectangle on the fly when user mouseover.
And im trying avoid to use UTFGrid for just highlighting. But I want to ensure the UTFGrid is the only way or not.
This sounds like the exact reason that UTFGrid was created! This site links to the tutorial that I used when learning UTFGrid, and it is solid.
Updated after your update:
MarkerCluster might have the look/feel you are going after, they basically paint a polygon onto the map layer. You can check the source here, and here's a relevant snippet:
_showCoverage: function (e) {
var map = this._map;
if (this._inZoomAnimation) {
return;
}
if (this._shownPolygon) {
map.removeLayer(this._shownPolygon);
}
if (e.layer.getChildCount() > 2 && e.layer !== this._spiderfied) {
this._shownPolygon = new L.Polygon(e.layer.getConvexHull(), this.options.polygonOptions);
map.addLayer(this._shownPolygon);
}
},