I am doing a choropleth map using leaflet and JSON data. I got a clean tutorial in leaflet site. In that tutorial the used us-states.js JSON file where the choropleth was based on the feature.properties.density feature of the JSON if I am not wrong. If we see the JS file we can found there is a field called "ID" which is state id. I have a database where I have 5 different categories of data based on state ID. Categories are population, density, male_population, female_population, literacy_rate. I am calling those data through ajax and get a GeoJSON data state-wise as follows (Its a dummy data).
[{"state_id":"01","population":"123456","density":"1234","male_pop":"65432","female_pop":"57421","literacy_rate":"98"}]
...
[{"state_id":"50","population":"123456","density":"1234","male_pop":"65432","female_pop":"57421","literacy_rate":"98"}]
I want to integrate this data as choropleth value. when I call on literacy function the variation of color will be based on literacy_rate data. I can make the changes in getcolor function category wise.
Thanks
I would recommend using Leaflet Data Visualization Framework (DVF) plugin, located here. This plugin contains a set of Layers, (such as a Choropleth layer) and a set of functions to help color-code your data.
The example located here will demonstrate the DVF Choropleth Data layer.
Using DVF, you can create a color function to create your scaling color values. For example, this function will generate a function from yellow to red. The point values contain the range of values, and the HSL Hue.
var yellowToRed = new L.HSLHueFunction(new L.Point(50, 60), new L.Point(100, 0));
DVF contains a L.ChoroplethDataLayer layer that will accept your GeoJSON, and your color function, and will generate the layer for you.
var layer = new L.ChoroplethDataLayer(geoJSON, {
// For the full options, see the documentation
displayOptions: {
// The display will be colored by your 'density' property in your GeoJSON. This accesses the feature object directory, so the 'properties' prefix is required if you're going to access a GeoJSON property on your data.
'properties.density': {
// A legend will automatically be generated for you. You can add this as a control. This displayName property will be the title for this layer's legend.
displayName: 'Density',
color: yellowToRed
}
};
});
This is just a simple example. For your data, it might look slightly different. Go through the DVF documentation and samples and you'll find what's right for you.
Related
This question is a follow-up from my previous post .
After successfully creating a World Bubble Map based on geolocation on Observable Notebook, with help from Chris.
I want to make my Notebook user-friendly. For instance, I have already some sets of data that are plotted as bubbles according to parameter selection. Now what I want to achieve is, if somebody wants to use their own set of data on my Observable Notebook I want their data to be plotted as bubbles based on geolocation of the countries (same as my data).(Make a path where they can upload their own data) And since the dropdown menu content has been manually modified according to the parameters name, will it be possible to make the dropdown menu content change automatically when someone adds a new set of data ?
How can I do something like this ? Any suggestions? Thank you in advance.
A link to my Notebook : Bubble Maps
This is partial solution with some open Issues :
I have created a second map where users can use their own set of data to plot the bubbles. Although, it will only work if the Country Code of uploaded .CSV data is the same as ISO_A2 (Country Code)from the .JSON file used in my Notebook. There are some issues I could not resolve. Mainly 1 . Some countries appear on the top left corner of the map which is not actually correct (the second map should actually plot the countries like the first map as both map has same longitude and latitude) 2. The same zoom function is working for the first map but not for the second map (that is kind of confusing me) 3. Will it be possible to generate input buttons automatically from the uploaded file, or should it be done manually every time. If I could get some help and suggestions, it would be so great. I have included a dummy data in my notebook (in the data paneāFile Attachments) which can be used to test the second map. Here is the link : Bubble Map
The Radio button code:
viewof userparameter = Inputs.select(new Map([ ["Area (Sq. Km)", "areasqkm"],["Population", "population"], ["Airports", "airports"],["Gold in tonnes","Goldtonnes"],["GDP in dollar","gdpdollar"] ]), { value: "Area (Sq. Km)", label: "User Parameter to show:" })
The zoom properties:
viewof userparameter = Inputs.select(new Map([ ["Area (Sq. Km)", "areasqkm"],["Population", "population"], ["Airports", "airports"],["Gold in tonnes","Goldtonnes"],["GDP in dollar","gdpdollar"] ]), { value: "Area (Sq. Km)", label: "User Parameter to show:" })
Background:
I'm currently integrating HERE maps into our web-based application. I'm trying both - HERE provided Javascript API and Leaflet at the same time to find the best approach for our use-case.
While JavaScript API provided by HERE maps is OK, rendering wise Leaflet performs much better when using raster tiles.
Issue:
It would be fine by me to use raster tiles + leaflet, but our application also needs to display traffic incidents data.
Traffic incident data is provided by HERE in JSON and XML formats (Documentation link, Example JSON). They provide [Z]/[X]/[Y], quadkey, prox, bbox, or corridor filters which can be used to retrieve filtered data set.
I've tried using [Z]/[X]/[Y] addressing with custom L.TileLayer implementation which loads appropriate JSON, converts it to GeoJSON and displays GeoJSON on map. However that approach is very inefficient and significant performance drop is visible.
Question:
Maybe anyone has already solved this issue and could share any insights on how the HERE traffic incidents could be shown on Leaflet map without encountering performance issues?
I created the following script, which works without any performance issues:
var fg = L.featureGroup().addTo(map);
function loadTraffic(data) {
fg.clearLayers();
var d = data.TRAFFICITEMS.TRAFFICITEM.map((r) => {
var latlngs = [];
if (r.LOCATION.GEOLOC) {
if (r.LOCATION.GEOLOC.ORIGIN) {
latlngs.push(L.latLng(r.LOCATION.GEOLOC.ORIGIN.LATITUDE, r.LOCATION.GEOLOC.ORIGIN.LONGITUDE));
}
if (r.LOCATION.GEOLOC.TO) {
if (L.Util.isArray(r.LOCATION.GEOLOC.TO)) {
r.LOCATION.GEOLOC.TO.forEach((latlng) => {
latlngs.push(L.latLng(latlng.LATITUDE, latlng.LONGITUDE));
})
} else {
latlngs.push(L.latLng(r.LOCATION.GEOLOC.TO.LATITUDE, r.LOCATION.GEOLOC.TO.LONGITUDE));
}
}
}
var desc = r.TRAFFICITEMDESCRIPTION.find(x => x.TYPE === "short_desc").content;
return {
latlngs,
desc
}
})
console.log(d);
d.forEach((road)=>{
L.polyline(road.latlngs,{color: 'red'}).addTo(fg).bindPopup(road.desc);
});
map.fitBounds(fg.getBounds())
}
If this script is not working for you, please share your json file.
Ok, so I've found a solution for this task. Apparently I was on a good path, I only needed to optimize my implementation.
What I had to do to achieve appropriate performance is:
Create custom CircleMarker extension which would draw custom icon on canvas
Create JS worker which would fetch the data from a given URL, transform it to GeoJSON and return GeoJSON to it's listener
Create custom GridLayer implementation, which, in fetchTile function, creates worker instance, passes it a link with appropriate [Z]/[X]/[Y] coordinates already set, adds listener, which listens for worker's done event and returns empty tile
On worker's done event, custom GridLayer implementation creates GeoJSON layer, adds it to the dictionary with coordinates as a key and, if zoom level is still the same - adds that layer to the map
Add zoomend observer on a map, which removes any layers that does not match current zoom level from the map
Now the map is definitely usable and works way faster than original HERE JS API.
P.S. Sorry, but I can't share the implementation itself due to our company policies.
There are many beautiful charts on Geographical Maps that can be drawn with AmCharts JS library as in https://www.amcharts.com/demos/#javascript-maps
However, I was wondering if it is possible to create a Custom Map. For example, I want to create a Global map with all Color-coded countries, but want to show US and Canada as one individual Country without any intermediate boundary between them. All other Countries should remain same.
Really appreciate for any pointer on above direction.
Thanks,
With amCharts v4, we have switched to using GeoJSON for our maps.
While I'm sure there's some kind of way to merge geographical polygons using mapshaper, I haven't tried it out myself yet and have been getting comfortable using the free software, QGIS.
There's already a tutorial out there on merging polygons if you're interested. I'll give a quick, specific rundown here anyway.
Download & install QGIS, then go ahead and grab the latest version of amCharts v4 worldLow.json (or worldHigh.json if you want more detail), in this case I've used worldLow.json:
https://github.com/amcharts/amcharts4-geodata/blob/master/dist/script/json/worldLow.json
Raw file:
https://raw.githubusercontent.com/amcharts/amcharts4-geodata/master/dist/script/json/worldLow.json
Make a copy of the .json file or otherwise rename it, it will be overwritten in the steps below.
Open QGIS and start a new project
Menu: Layer -> Add Vector Layer -> Source(s) -> Vector Dataset(s): Choose above file -> Add
Enable editing on the layer by either: Clicking pencil icon, Menu: Layer -> Toggle Editing, or in the bottom left Layers panel right click the layer and select Toggle Editing in the context menu
Menu: Edit -> Select -> Select feature(s)
Select the US and Canada polygons.
Menu: Edit -> Merge selected features (near the bottom)
Save your project, save layer edits, now your json file will be updated.
It should look something like this:
https://gist.github.com/notacouch/485b8525a360c15690f1ab23cbf04940
Now to refer to this in your map, you can do so via:
// Create map polygon series
var polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
polygonSeries.geodataSource.url = "https://gist.githubusercontent.com/notacouch/485b8525a360c15690f1ab23cbf04940/raw/e6742c279571ae02166027817351a4250b1bea69/worldLow--canadia.json";
In addition, you can generate a new color per country by using the default ColorSet, e.g.
// Have amCharts generate a new color for each mapPolygon once they're available
//
// Learn more about Event Listeners:
// {#link https://www.amcharts.com/docs/v4/concepts/event-listeners/}
polygonSeries.events.on("datavalidated", function() {
polygonSeries.mapPolygons.each(function(polygon, index) {
// Learn more about ColorSets:
// {#link https://www.amcharts.com/docs/v4/concepts/colors/#Color_sets}
polygon.fill = chart.colors.getIndex(index);
});
});
// Create hover state and set alternative fill color
//
// Learn more about States:
// {#link https://www.amcharts.com/docs/v4/concepts/states/}
var hs = polygonTemplate.states.create("hover");
// Darken the polygon's current color
//
// Learn more about Adapters:
// {#link https://www.amcharts.com/docs/v4/concepts/adapters/}
hs.adapter.add("fill", function(fill) {
return fill.brighten(-0.2);
});
Here's a demo with all that thrown together:
https://codepen.io/team/amcharts/pen/abd95bebcfb65a74d9472043d63351fc
That's probably undesirable, you can also color via data binding, or via a heat map.
For customizing maps in v4 in general, I recommend checking out our guide on Creating custom maps. Between mapshaper, QGIS, and all the map-related files out there, there's bound to be a way to get the customization you want and possibly in short order, too.
Hope this helps.
I have two geoJSON feature collections that I need to add to the map, and I also want them to be toggled on and off via the layer visibility controllers as shown in http://leafletjs.com/examples/layers-control.html
how can I do this?
There is also a very good tutorial on the usage of L.GeoJSON, Leaflet's GeoJSON layer, which can be found here: http://leafletjs.com/examples/geojson.html and here is the reference for L.GeoJSON: http://leafletjs.com/reference.html#geojson You already found the tutorial on L.control.layers, here is the reference for it: http://leafletjs.com/reference.html#control-layers
It's actually quite simple to do, it's just a matter of creating a layercontrol, loading a GeoJSON file into your script by using your favorite XHR library, use the retrieved data to defined a L.GeoJSON layer and add it to the layercontrol. In code:
// Create the layercontrol and add it to the map
var controlLayers = L.control.layers().addTo(map);
// Loading a GeoJSON file (using jQuery's $.getJSON)
$.getJSON('/my-folder/my-file.json', function (data) {
// Use the data to create a GeoJSON layer and add it to the map
var geojsonLayer = L.geoJson(data).addTo(map);
// Add the geojson layer to the layercontrol
controlLayers.addOverlay(geojsonLayer, 'My GeoJSON layer title');
});
A working example on Plunker: http://plnkr.co/edit/tFVrrq?p=preview
Since you create a layer when loading a GeoJSON, you can add it to the layer control as an Overlay Layer (simply modify that example and replace the cities layer.
I am using the heatmap.js library from pa7 in github successfully to make some heatmap
http://www.patrick-wied.at/static/heatmapjs/example-heatmap-leaflet.html
But when I updated the heatmap with additional data or new data points, the leaflet library does not really update with the additional data points. There seems to be no method that can used for redraw. I dont know if someone has already solved this problem, thats why I am asking to see. Here is the simple portion of my additional script:
ndata=[{lat: 13.59, lon:-17.05, value: 11},{lat: 33.08, lon:-103.24, value: 19}]
testData.data.push.apply(ndata)
layer.addData(testData.data);
There is no errors in the console of Google chrome or no exceptions.
Thanks
There is a redraw method in the layer:
var heatmapLayer = L.TileLayer.heatMap();
// add layer to map
heatmapLayer.redraw()
Having said this, in the latest version, the heatmap layer automatically redraws when you set the data (see https://github.com/pa7/heatmap.js/blob/d09c4e99852b4849e2b4d4f12976a9fce0327ca5/src/heatmap-leaflet.js#L112).