Map Visualization in Zeppelin - atodkar/blog GitHub Wiki
Visualization tools are getting matured in Zeppelin and we can have Helium extensions to extend our visualizations for various scenarios. I wanted to get some visualization on Zeppelin on maps and show data points plot for various algorithms we were using in our Data Analytics scripts. I was using pyspark for analytics and wanted to quickly see results of algorithm by plotting maps on zeppelin. I see Zeppelin implicitly support angular interpreter, so there are two ways to visualize data using Leaflet maps
- Use Helium Leaflet map Plugin
- Use Javascript code to manually plot graph
In Zeppelin 0.8, Helium supports extensions registry and we can select Leaflet from Visualization section.
As shown in screen, we can enable many more extensions to enable better visualization and extend capabilities of Zeppelin. We can also write our own extensions which will help in solving specific use case.
To enable leaflet-map, go to next page in visualization and click enable. We will need to refresh the page for now to get this in effect. After Zeppelin is restarted and page is refreshed we see an additional option in visualization section of Zeppelin for maps.
%spark.pyspark
#Latitude and longitude data
latLong = [{'lat': -34.558, 'long':-58.66}, {'lat': -34.560, 'long':-58.66}, {'lat': -34.562, 'long':-58.66}, \
{'lat': -34.564, 'long':-58.66}, {'lat': -34.566, 'long':-58.66}, {'lat': -34.568, 'long':-58.66}, {'lat': -34.570, 'long':-58.66}]
latLongDF = sc.parallelize(latLong).toDF()
z.show(latLongDF)
We will need to go in settings to map datapoints with latitude, longitude and header and the map gets plotted with automatic zoom set to accommodate all points provided in dataframe. This provides with nice UI in visualization section and we can select points to be shown on map using settings and we are done. This is the most simple and straight forward way but comes with limitation that we can only show points with bookmark sign on map.
In case we need to see more granular details by plotting specific shapes such as circle, rectangle etc. then we need to use a little crude way in which the Angular code can be used to show maps and we can pass data between pyspark and Angular using Zeppelin Context.
Below code shows how the zeppelin can be used to show points and other shapes on leaflet graph. The key factor here is to pass data between pyspark and Angular interpreter.
%pyspark
#Latitude and longitude data
latLong = [{'lat': -34.558, 'long':-58.66}, {'lat': -34.560, 'long':-58.66}, {'lat': -34.562, 'long':-58.66}, \
{'lat': -34.564, 'long':-58.66}, {'lat': -34.566, 'long':-58.66}, {'lat': -34.568, 'long':-58.66}, {'lat': -34.570, 'long':-58.66}]
z.z.angularBind("latLong", latLong)
# Rectangle
rect = [[-34.560, -58.65], [-34.564, -58.67]]
z.z.angularBind("rectangle", rect)
Here we are generating some sample lat long data which will be used to plot shapes on leaflet map in angular interpreter. Add this data to zeppelin context in order to have it available to angular. Notice two z to refer to the parent context.
%angular
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<div id="map" style="height: 800px; width: 100%"> </div>
<script type="text/javascript">
function initMap() {
var scope = angular.element($("#map").parent('.ng-scope')).scope().compiledScope;
var map = L.map('map').setView([-34.564, -58.66], 14);
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png").addTo(map);
var myRenderer = L.canvas({ padding: 0.5 });
for(var key in scope.latLong) {
// draw circles on Lat Long provided
L.circleMarker([scope.latLong[key].lat, scope.latLong[key].long], {
renderer : myRenderer,
radius: 10,
color: "red"
}).addTo(map);
}
// Draw rectangle
L.rectangle(scope.rectangle, {
renderer : myRenderer,
color: "blue"
}).addTo(map);
}
if (window.L) {
initMap();
} else {
console.log('Loading Leaflet library');
var sc = document.createElement('script');
sc.type = 'text/javascript';
sc.src = 'https://unpkg.com/[email protected]/dist/leaflet.js';
sc.onload = initMap;
sc.onerror = function(err) { alert(err); }
document.getElementsByTagName('head')[0].appendChild(sc);
}
</script>
This code reads data from zeppelin context using line angular.element($("#map").parent('.ng-scope')).scope().compiledScope
. This reads zeppelin context's scope and provides us as a variable to access contents. Same scope can be accessed from HTML to show variables using angular syntax.
Below image shows map and points defined in map -