Closed jchailloux closed 7 years ago
Could you please export your READ app -- make a gist out of it -- and give me a link to the gist? This will help me understand what your app is doing... You can export an app by going to the Apps tab and clicking on the up arrow in the sidebar -- which copies your READ app as a JSON object for others to import.
Here is the json file.
I don't have the full picture since I don't have access to data produced by ws://localhost:56652 and ws://localhost:56651.
But after importing your app, one thing I noticed right away was that in your Geofenses dataset, you have a transform function that uses state (parent dataset = Raw; latest value from Raw = x; current state = s), but state is not enabled and initialized.
Could you please enable the state and set to some valid initial value (is s an array? If so, perhaps an empty array [] is a valid initial value in your situation)? This is briefly described in the usage info tab of the transform dataset.
Alternately, your Geofenses transform could also look like this without any state:
x => x.tuples[0].tuple.geofences
I also noticed that you have Pie, Multibar Horizontal, and Leaflet Map visualizations defined for Geofences dataset. While Pie and Multibar Horizontal require the data to be an array, Leaflet Map requires the data to be an object. So, the three of them are not going to work on the same dataset. You can know more about the data formats by looking into the usage info for each visualization, and also by examining the sample app bundled with READ which includes a dataset for every supported READ visualization.
The leaflet map should use the GeoJSON dataset.
I sent yo the copy of the data part on the dataset page.
x =>{ return { center: { lat: 55.618881, lng: 12.079211, zoom: 1
},
defaults: {
scrollWheelZoom: false
},
geojson: {
data:x.tuples[0].tuple,
style: {
fillColor: '#ccfa12',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
}
}
}
When I paste your dataset into a raw READ dataset, I see stuff like this: "features": "{\"type\": \"FeatureCollection\",\"features\":
... which is invalid json. Could you please verify / ensure that your READ dataset is producing valid json -- in particular, there should be quotes here and not escaped quotes. In fact, the value for features key seems to be a string and not a JSON object as one would expect...
JSON.parse(x.tuples[0].tuple.features) does the trick.
How can have the same capability such as changing the color according to the number of entities inside like I did in the areas.html ? function getColor(d) { return d > 6000 ? '#800000' : d > 5000 ? '#800026' : d > 4000 ? '#BD0026' : d > 3000 ? '#E31A1C' : d > 2000 ? '#FC4E2A' : d > 1000 ? '#FD8D3C' : d > 500 ? '#FEB24C' : d > 100 ? '#FED976' : '#FFEDA0'; }
What about a popup onmouseover ?
For styling... here is an idea. This assumes that you want to style each feature based on feature.properties.number
-- if you want to use a different property, e.g., feature.properties.density
modify this function appropriately (and also modify the properties object appropriately so that it contains the data you need for the style function).
Transform your geojson using the following logic...
/* x is the geojson */
x => {
/* We are setting a style function for the geojson -- this is something angular-leaflet-directive understands */
x.geojson.style = function style(feature) {
/* We want to style each feature based on feature.properties.number */
let d = feature.properties.number;
return {
/* set fillColor based on feature.properties.number */
fillColor: d > 6000 ? '#800000' :
d > 5000 ? '#800026' :
d > 4000 ? '#BD0026' :
d > 3000 ? '#E31A1C' :
d > 2000 ? '#FC4E2A' :
d > 1000 ? '#FD8D3C' :
d > 500 ? '#FEB24C' :
d > 100 ? '#FED976' :
'#FFEDA0',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
};
return x;
}
Here's the relevant leaflet documentation: http://leafletjs.com/reference.html#geojson
Could you please also try using the onEachFeature call back (in addition to the style callback) to bind a popup? Not 100% sure this will do the trick -- but hopefully angular-leaflet-directive (which READ uses) supports this callback as well (I know per-feature styling is supported)
I miss the point. In the same transform or do I have to transform again x from the previous transform ?
Either way is fine... ultimately, we want the data used by the visualization to contain the style callback. So you can do it in the same function or in a new function.
Great
this works x =>{ return { center: { lat: 55.618881, lng: 12.079211, zoom: 14
},
defaults: {
scrollWheelZoom: false
},
geojson: {
data:JSON.parse(x.tuples[0].tuple.features),
style: function style(feature) {
/* We want to style each feature based on feature.properties.number */
let d = feature.properties.number;
return {
/* set fillColor based on feature.properties.number */
fillColor: d > 6 ? '#800000' :
d > 5 ? '#800026' :
d > 4 ? '#BD0026' :
d > 3 ? '#E31A1C' :
d > 2 ? '#FC4E2A' :
d > 1 ? '#FD8D3C' :
d > 500 ? '#FEB24C' :
d > 100 ? '#FED976' :
'#FFEDA0',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
}
}
}
}
Perfect. Just out of curiosity... can you show me your new map also?
What about the mouse over could we have this function available to update ? Could we display the legend for colors ?
legends are already supported... please look into the maps dashboard that is part of the sample app (included when you download READ).
For binding a popup on mouseover... could you please experiment with the onEachFeature callback as I mentioned earlier. Leaflet documentation is here: http://leafletjs.com/reference.html#geojson (again I'm not 100% certain this solution works since I'm yet to test this).
In the areas.html I did that. The question is more about where to write the code I want.
function style(feature) { return { weight: 1, opacity: 1, color: 'white', dashArray: '1', fillOpacity: feature.properties.density+0.5, // fillOpacity: 0.6, fillColor: getColor(feature.properties.number) }; }
function highlightFeature(e) { var layer = e.target;
layer.setStyle({
weight: 1,
color: '#000',
dashArray: '',
fillOpacity: 0.9
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
function resetHighlight(e) { geojsonLayer.resetStyle(e.target); info.update(); }
function zoomToFeature(e) { map.fitBounds(e.target.getBounds()); }
function onEachFeature(feature, layer) { layer.on({ mouseover: highlightFeature, mouseout: resetHighlight, click: zoomToFeature }); }
var geojsonLayer; var legend = L.control({position: 'bottomright'});
onEachFeature would be a assigned to geojson just like style... geojson will now have data, style, and onEachFeature, with the last two being functions. You can try this in the same transform where you are adding the style function.
onEachFeature looks with this code
x =>{ return { center: { lat: 55.618881, lng: 12.079211, zoom: 14
},
defaults: {
scrollWheelZoom: false
},
geojson: {
data:JSON.parse(x.tuples[0].tuple.features),
style: function style(feature) {
/* We want to style each feature based on feature.properties.number */
let d = feature.properties.number;
return {
/* set fillColor based on feature.properties.number */
fillColor: d > 6 ? '#800000' :
d > 5 ? '#800026' :
d > 4 ? '#BD0026' :
d > 3 ? '#E31A1C' :
d > 2 ? '#FC4E2A' :
d > 1 ? '#FD8D3C' :
d > 500 ? '#FEB24C' :
d > 100 ? '#FED976' :
'#FFEDA0',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
},
onEachFeature: function onEachFeature(feature, layer) {
layer.on({
mouseover: function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 1,
color: '#000',
dashArray: '',
fillOpacity: 0.9
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
, mouseout: function resetHighlight(e) { geojsonLayer.resetStyle(e.target); info.update(); } , click: function zoomToFeature(e) { map.fitBounds(e.target.getBounds()); } }); } } } }
return {
center: {
lat: 55.618881,
lng: 12.079211,
zoom: 14
},
defaults: {
scrollWheelZoom: false
},
geojson: {
data:JSON.parse(x.tuples[0].tuple.features),
style: function style(feature) {
/* We want to style each feature based on feature.properties.number */
let d = feature.properties.number;
return {
/* set fillColor based on feature.properties.number */
fillColor: d > 6 ? '#800000' :
d > 5 ? '#800026' :
d > 4 ? '#BD0026' :
d > 3 ? '#E31A1C' :
d > 2 ? '#FC4E2A' :
d > 1 ? '#FD8D3C' :
d > 500 ? '#FEB24C' :
d > 100 ? '#FED976' :
'#FFEDA0',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
},
onEachFeature: function onEachFeature(feature, layer) {
layer.on({
mouseover: function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 1,
color: '#000',
dashArray: '',
fillOpacity: 0.9
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
, mouseout: function resetHighlight(e) { geojsonLayer.resetStyle(e.target); info.update(); } , click: function zoomToFeature(e) { map.fitBounds(e.target.getBounds()); } }); } } } }
The info is not display. Do you have an other great idea ?
var info = L.control();
info.onAdd = function (map) { this._div = L.DomUtil.create('div', 'info'); this.update(); return this._div; };
info.update = function (props) { this._div.innerHTML = '
For legend I have some issues
I also notice that the mouseout and click don't work.
I see... Please try importing this new sample app: https://gist.github.com/sriumcp/9b9ecc058b1e9b3f505d7bbf335b4cf5
I will fix the app bundled with READ soon.
Legend is ok now thanks
Could we avoid to set center: { lat: 55.618881, lng: 12.079211, zoom: 14
},
at all time and then let the user zomm in or out and move ? At all time it goes back to the "default"
Two ideas.
Could you please export your current READ app and give me a link... I can try experimenting with mouseover and mouseout and click.
Demo_READ.json.zip I sent you the streams application on email
StreamsUI.zip This is the node application I would like to move on the toolkit (in case of)
Could you please replace your onEachFeature function with the below function? This is the angular leaflet way of manipulating the map object (sorry -- its not yet fully documented in READ; I'm planning to switch from angular-leaflet to the regular Leaflet library in the next release to make map making in READ simpler)...
onEachFeature: function onEachFeature(feature, layer) {
layer.on({
mouseover: function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 1,
color: '#000',
dashArray: '',
fillOpacity: 0.9
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
},
mouseout: function mouseOut(e) {
leafletData.getGeoJSON().then(geojson => {
geojson.resetStyle(e.target);
})
},
click: function zoomToFeature(e) {
leafletData.getMap().then(map => {
map.fitBounds(e.target.getBounds());
});
}
});
}
This solved the mouse issues. The pending issues are info not display on mouseover and removed on mouseout and zoom/center always reset.
Don't be sorry READ looks so promises.
I will go thru the cluster html and see how to convert. I will go back for questions and/or solutions
Re: the zoom/center, did you move the center into the defaults property, or is it still at the top level?
defaults: {
scrollWheelZoom: false,
center: {
lat: 55.618881,
lng: 12.079211,
zoom: 14
},
},
still in the x transform
switching to default solve the issue thanks
How about this function? Of course, you will need to change the popup content and latlng based on feature properties...
onEachFeature: function onEachFeature(feature, layer) {
layer.on({
mouseover: function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 1,
color: '#000',
dashArray: '',
fillOpacity: 0.9
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
leafletData.getMap().then(map => {
var popup = L.popup()
.setContent('<p>Hello world!<br />This is a nice popup.</p>')
.setLatLng(L.latLng(55.618881, 12.079211))
.openOn(map);
});
},
mouseout: function mouseOut(e) {
leafletData.getGeoJSON().then(geojson => {
geojson.resetStyle(e.target);
});
leafletData.getMap().then(map => {
console.log(map);
map.closePopup();
});
},
click: function zoomToFeature(e) {
leafletData.getMap().then(map => {
map.fitBounds(e.target.getBounds());
});
}
});
}
I updated the popup as follow. I have some issues (some blink popup) but I guess it's because I have fences inside fences.
leafletData.getMap().then(map => {
var popup = L.popup()
.setContent('<h4>Areas Density</h4>'+'<i>'+new Date(feature.properties.ts).toGMTString()+'</i><br/>'+'<b>' + feature.properties.name + '('+ feature.properties.OBJECTID+') : '+parseInt( feature.properties.area ).toLocaleString()+' m<sup>2</sup></b><br/>' + feature.properties.number + ' people<br />' + feature.properties.density.toLocaleString(undefined, { minimumFractionDigits: 4 }) + ' people / m<sup>2</sup>' )
.setLatLng(L.latLng(feature.geometry.coordinates[0][0][1], feature.geometry.coordinates[0][0][0]))
.openOn(map);
});
thanks
could I get the upper right latlon to have the popup at the same place ?
The last READ app
Yes, I notice the flickering as well. Could you try L.control instead of pop up -- now that you know how to get a reference to the map object? Going back to your original idea (of adding an info div) instead of a popup might resolve this issue.
I'm trying to migrate the Cluster.html on visualization. I'm having issues because it looks like the marker definition is not valid.
using this transform x => { / taipei: { layer: "northTaiwan", lat: 25.0391667, lng: 121.525, } / var markers='{'; var first=1; for (var value of x.tuples[0].tuple.locations) { markers = markers+(first == 0?',':'')+value.id+':{lat:'+value.latitude+',lng:' +value.longitude+'}'; first=0; } markers = markers+'}'; // var markers = leafletData.getMap().markerClusterGroup(); // L.markers.clearLayers(); return{ defaults: { scrollWheelZoom: false, center: { lat: 55.618881, lng: 12.079211, zoom: 14
}
},
markers
}
}
This is the output { "defaults": { "scrollWheelZoom": false, "center": { "lat": 55.618881, "lng": 12.079211, "zoom": 14 } }, "markers": "{142:{lat:55.61476346106971,lng:12.06946614470684},14:{lat:55.615240079731045,lng:12.08811528552074},161:{lat:55.61174290104943,lng:12.077519594403434},20:{lat:55.62208372595761,lng:12.097116836162703},146:{lat:55.62137107740439,lng:12.08397103090601},134:{lat:55.621323562892904,lng:12.077009278061105},196:{lat:55.62424874263861,lng:12.090774580333495},166:{lat:55.61450581129024,lng:12.075062586655413},24:{lat:55.61044152035306,lng:12.092445946702231},3:{lat:55.61921674935665,lng:12.096485266871463}}" }
I tried this without success leafletData.getMap().then(map => { var info = L.control({position: 'bottomleft'}); var _div = L.DomUtil.create('div', 'info'); _div.innerHTML = '
Ok... two points:
The markers object is not an object at all but is a string right now which is a problem... markers are supported with proper objects not strings.... below is an example of properly defined markers...
{
center: {
lat: 52.52,
lng: 13.40,
zoom: 4
},
markers: {
m1: {
lat: 52.52,
lng: 13.40
}
}
}
When you share your READ app next time -- could you please replace the ws://... (Websocket) datasets with Raw datasets with the appropriate JSON data pasted in them? I know this kills all the dynamics and makes the app static -- but this is useful for sharing and debugging (I am not set up to run your Streams app properly; this makes it easier to debug the READ app).
This should contains raw dataset for the RawClusterStack
With the following transform function for ClusterStackTrans...
x => {
var markerObject = {};
x.tuples[0].tuple.locations.map(y => {
markerObject[y.id] = {
lat: y.latitude,
lng: y.longitude,
message: "this place is on earth",
group: "Europe"
};
});
return{
defaults: {
scrollWheelZoom: false,
center: {
lat: 55.618881,
lng: 12.079211,
zoom: 14
}
},
markers: markerObject
}
}
This map is the result....
Here is the documentation for angular-leaflet-directive which READ uses, and especially the documentation for the markers attribute: https://github.com/tombatossals/angular-leaflet-directive/blob/master/doc/markers-attribute.md (please also click on the Usage Info tab of the READ visualization to see how READ utilizes the various angular-leaflet-directive attributes).
I need to update and commit a new version of READ in order to support marker clustering (essentially include this leaflet plugin as a READ dependency). I can let you know once I have this up (you can upgrade to the latest version of READ once I do this).
I did the same for the GeoJSONRaw regarding the popup and div info
For the Cluster I have MarkerCluster plugin not loaded
Yes, I am aware of the MarkerCluster plugin issue... fix is on the way!
When it will work it could be nice to create a "migration" guide base on the 2 samples. I will work on and let you know to improve the doc ans samples.
Marker clusters are now supported in the latest version of READ (0.7.1)...
Here's the updated transform function.
x => {
var markerObject = {};
x.tuples[0].tuple.locations.map(y => {
markerObject[y.id] = {
lat: y.latitude,
lng: y.longitude,
group: "Europe"
};
});
return {
defaults: {
scrollWheelZoom: false,
center: {
lat: 55.618881,
lng: 12.079211,
zoom: 14
}
},
markers: markerObject
}
}
Notice that there is only a single change -- a new group property which got added to the markers (you can set the group as anything you want in this example).
Here's the result...
I will provide a quick list of steps for migration here (if you wish to help improve the documentation by creating a wiki page which properly documents these steps, you are very welcome to do so. Please issue a PR for the same).
I have few sample that use leaflet and I'm not able to create them using the toolkit.
Is anyone knows how to ?
areas.html uses geofences.txt cluster.html uses cluster.txt
Sample.zip
data.zip
This is one of the x transform function I tried on the dataset for areas
x =>{ return { center: { lat: 55.618881, lng: 12.079211, zoom: 1
}