Leaflet and D3js: Feature.properties are not showing in Leaflet popup - d3.js

How do you add feature properties under a D3 chart in a Leafletjs popup?
I got the popup and chart working, but I can't seem to add the feature.properties below the chart.
Here's a sample of my geoJSON data:
var myData = [{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Name":"Gulran","Province":"Hirat","Ethnic1":0.19,"Ethnic2":0.32,"Ethnic3":"0.10","Ethnic4":"0.00","Ethnic5":"0.10","Ethnic6":"0.00"},"geometry":{"type":"Polygon","coordinates":[[[60.941162109375,29.897805610155874],[61.92993164062499,31.034108344903512],[63.34716796874999,31.3348710339506],[64.05029296875,30.401306519203583],[64.412841796875,29.735762444449076],[64.09423828125,29.36302703778376],[62.29248046875,29.36302703778376],[60.941162109375,29.897805610155874]]]}},{"type":"Feature","properties":{"Name":"Chahar Burjak","Province":"Nimroz","Ethnic1":0.25,"Ethnic2":0.12,"Ethnic3":0.03,"Ethnic4":0.01,"Ethnic5":"0.00","Ethnic6":"0.00"},"geometry":{"type":"Polygon","coordinates":[[[63.38012695312499,31.3348710339506],[65.06103515625,31.80289258670676],[65.6982421875,31.156408414557],[66.016845703125,30.467614102257855],[65.291748046875,30.164126343161097],[64.22607421875,30.0405664305846],[63.38012695312499,31.3348710339506]]]}}]}];
Here's my popup code:
var popup = L.popup({minWidth: 600}).setContent(div);
layer.bindPopup(popup + '<br>' + feature.properties.NAME);
Here's my jsfiddle code to test. As you can see, the popups are working, but can't get the feature properties to display under the chart.
Thanks for any help...

I'm seeing multiple possible issues.
You are opening a <svg> element, but you are not closing it. If you add text like this it will get "swallowed" and end up within the rendered <svg>here</svg>, but the chart drawn by d3 will hide it:
// won't show the text
var div = $('<div id="chart" style="width: 600px; height: 400px;"><svg>here is some text</div>')[0];
You can fix that by using a self-closing <svg/>:
var div = $('<div id="chart" style="width: 600px; height: 400px;"><svg/><br/>here is some text</div>')[0];
Next issue is that you are trying to combine a L.popup object with strings:
layer.bindPopup(popup + 'here is some text');
Because this L.popup is an object you can not simply concatenate text to it. The result would look like this: "[object Object]here is some text".
And then you are using feature.properties.NAME which will not work because your GeoJSON properties' keys are named Name (notice the capitalization) - use instead: feature.properties.Name
To conclude, to solve your problem change the one line var div = ... in your onEachFeature_LMA function to this:
var div = $('<div id="chart" style="width: 600px; height: 400px;"><svg/><br/>'+feature.properties.Name+'</div>')[0];
Since you are using jQuery, if you want to add more graphs, other HTML elements later you can also build the popup's HTML like this:
var div = $('<div id="chart" style="width: 600px; height: 400px;"><svg/></div>')[0];
var div2 = $('<div></div>').html(feature.properties.Name);
$(div).append($(div2));

Related

compare class of path elements to a dataset

I am really new to d3 and I want to make a choropleth map with the d3 datamaps so if have to compare the classes of the pathways to my dataset. So far I did manage to get the path's
html
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="/d3map/datamaps.world.min.js"></script>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>
<script src="d3map.js"></script>
js code
var map = new Datamap({element: document.getElementById('container'),
})
var country = d3.selectAll("path")
Country is an array containing 177 elements
one element in country has this form:
<path d="bunch of numbers" class="datamaps-subunit AFG" style="etc"></path>
i want to see if the country code in the class (e.g. AFG) matches one in my dataset.
Country.class gives undefined
I am probably missing something really obvious
Here's an example fiddle: https://jsfiddle.net/g2v5sgmz/
You can filter specific classed elements in an array with the filter method like this:
var country = d3.selectAll("path");
var afg = country.filter(function(d,i){
if( d3.select(this).classed("AFG") ){
return d3.select(this);
}
})

Polymer 1.0 / leaflet-map 1.0: lat/long not updating on moveend event

I have a map element in a section of an HTML page using Polymer 1.0 and leaflet-map 1.0:
<section data-route="Page">
<div flex>
<my-maps id="mymap" flex></my-maps>
</div>
</section>
I am trying to add an event to the element that will pass the lat/long of the center coordinates to the console window anytime the map is panned, similar to the 'event' example given here: Polymer Leaflet demo
The problem is that the Lat/Long coordinates do not update; I only see the center coordinates of the starting center point. The map should open initially centered on a geolocated point, with a marker at that point.
I have tried adding the event script to the main index page, and to the element registration as so:
<script>
Polymer({
is: "my-maps",
ready: function () {
L.Icon.Default.imagePath="../../bower_components/leaflet/dist/images";
},
listeners:{
'moveend': 'testmove'
},
testmove: function(e){
var text = "Center: " + this.latitude.toFixed(9)
+ ", " + this.longitude.toFixed(9);
console.log(text);
}
});
</script>
... But I get the same result in both cases; the event fires, but only the initial center point coordinates are passed to the console.
Here is the HTML for my-maps element:
<dom-module id="my-maps">
<style>
:host {
height: 100%;
width:100%;
overflow:hidden;
}
leaflet-map {
position:absolute;
height: 900px;
width:100%;
overflow:hidden;
}
</style>
<template>
<leaflet-map latitude="{{latitude}}" longitude="{{longitude}}" zoom="14">
<leaflet-geolocation enable-high-accuracy latitude="{{latitude}}" longitude="{{longitude}}">
</leaflet-geolocation>
<template is="dom-if" if="{{latitude}}">
<leaflet-marker latitude="{{latitude}}" longitude="{{longitude}}">
</leaflet-marker>
</template>
</leaflet-map>
</template>
(Polymer registration here)
</dom-module>
I tried adding the listener to the leaflet-map tag as well (on-moveend), and I tried moving the registration inside and outside of dom-module element, but same result. I added an id to the map and assigned that lat and long to that instance as Ricky suggests, but now the geolocation point moves to the center on each map pan (where it should remain on the geolocated point.)
To make the marker stay on the geo-located point, but update the center co-ords, simply add an id to the leaflet-map and grab the latitude and longitude attributes:
<leaflet-map id="map">
...
testmove: function(e) {
var text = 'Center: ' + this.$.map.latitude.toFixed(9)
+ ', ' + this.$.map.longitude.toFixed(9);
console.log(text);
}
I essentially answered my extended question here. My solution involved editing the leaflet-map element itself, by adding a one-time listener for the initial latitude and longitude if given by the geolocator. If the geolocator fails, the map pans to a default location. I removed the latitude and longitude property from the leaflet-map element tag in my custom map element.

how to use html content inside a canvas element

Can any one tell me how to place my html content on a canvas.And if we can do that, will the properties and events of those elements works or not, and also I have animations drawn on that canvas.
From this article on MDN:
You can't just draw HTML into a canvas. Instead, you need to use an
SVG image containing the content you want to render. To draw HTML
content, you'd use a element containing the HTML, then
draw that SVG image into your canvas.
It than suggest you follow these steps:
The only really tricky thing here—and that's probably an
overstatement—is creating the SVG for your image. All you need to do
is create a string containing the XML for the SVG and construct a Blob
with the following parts.
The MIME media type of the blob should be "image/svg+xml".
The element.
Inside that, the element.
The (well-formed) HTML itself, nested inside the .
By using a object URL as described above, we can inline our HTML
instead of having to load it from an external source. You can, of
course, use an external source if you prefer, as long as the origin is
the same as the originating document.
The following example is provided (you can see more information about this in this blog by Robert O'Callahan):
DEMO
const ctx = document.getElementById("canvas").getContext("2d");
const data = `
<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'>
<foreignObject width='100%' height='100%'>
<div xmlns='http://www.w3.org/1999/xhtml' style='font-size:40px'>
<em>I</em> like <span style='color:white; text-shadow:0 0 2px blue;'>CANVAS</span>
</div>
</foreignObject>
</svg>
`;
const img = new Image();
const svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
const url = URL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
};
img.src = url;
<canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
This example results in this HTML being rendered to canvas as this:
Will the properties and events of those elements works or not ?
No, everything drawn to a canvas is forgotten as passive pixels - they becomes simply an image.
You will need to provide custom logic that you provide yourselves in order to to handle any such things as clicks, objects, events etc. The logic need to define the areas, objects and anything else.

MouseOver or hover() or css() for changing positions

I have an image on my page and want that it change the position on mouseover or on click, so I tried several things but I cant find the right start.
So I started to change the top-position but that's not working, I would like that on mouseover the image it jumps to position x/y (maybe random x/y) if I mouseover it again it will jump to another position.
$('#image').one('click', function () {
$(this).css( 'top' : '+=200' );
});
But that's not working so please someone could give me some input that I can figure out what to do?
Note that while setting values for top, jQuery will not automatically get the value of top set previously and add 200 to it(like a normal programming language). You could have seen this as an error in executing if you try to see in a console. You can get the value of top first and then add 200px to it and then set it again.
This should work:
$('#image').one('click', function () {
var top = parseInt($(this).css("top"));
$(this).css( 'top' , top+ 200);
});
I had to parse the string to an integer returned by css, you could also substitute that extra operation by some method that returns only the value and not the value with "px" added to it, dunno if there is one already.
Try something like this:
<html>
<head>
<script type="text/javascript">
function switchPos(obj)
{
var divobj = document.getElementById('div1')
var x = divobj.offsetHeight - obj.height - 5;
var y = divobj.offsetWidth - obj.width - 5;
var randomx = Math.floor(Math.random()*x+1);
var randomy = Math.floor(Math.random()*y+1);
obj.style.top = randomx + 'px';
obj.style.left = randomy + 'px';
}
</script>
</head>
<body>
<div id="div1" style="width: 800px; background: red;">
<img src="image.jpg" style="position:relative;" onmouseover="switchPos(this);" onmouseout="switchPos(this);"/>
</div>
</body>
</html>

Attach keyboard events to html5 canvas

It looks like mouse events will add listeners to canvas elements fine, but keyboard events don't seem to be working for canvas elements.
Example:
http://jsfiddle.net/H8Ese/1/
Browsers:
Chrome 14.0
FF 5.0.1
I know I can use the document level listeners, but I'm trying to get the Canvas element first (so that your keyboard works everywhere else on the page).
Any ideas on how to get key event listening working on canvas elements?
I don't think you can add keyboard event listener directly to the canvas. If you don't want to register event handler on window level then I think you can wrap the canvas inside a div and register keyboard events on the div.
<div id="canvasWrapper" style="border:1px solid; width:600px; height:400px;">
<canvas id="canvas" width="600" height="400" >
Could not create Canvas!
</canvas>
</div>
jQuery("#canvasWrapper").keypress(function(e){
keys[e.keyCode] = true;
alert("key pressed!");
});
Another interesting way is to use tabIndex on the canvas tag and bind keypress on the canvas. I have updated the code at jsfiddle, pasting here too for future references.
<canvas id="my-canvas" tabindex="1"></canvas>
$("#my-canvas").bind({
keydown: function(e) {
var key = e.keyCode;
var elem=document.getElementById("my-canvas");
var context=elem.getContext("2d");
context.font = "bold 20px sans-serif";
context.clearRect(0,0,300,200);
context.fillText("key pressed " + key, 10,29);
},
focusin: function(e) {
$(e.currentTarget).addClass("selected");
},
focusout: function(e) {
$(e.currentTarget).removeClass("selected");
}
});
$("#my-canvas").focus();

Resources