Support Questions
Find answers, ask questions, and share your expertise

Bind leaflet map with angular and scala in Apache Zeppelin

I am trying to adapt the example I found here: https://gist.github.com/granturing/a09aed4a302a7367be92 to my current requirements.

This is what I put in the spark paragraph:

%spark2 
val COSMODE_Wind = sqlContext.sql("SELECT lat0, long0, value FROM cosmode_single_level_elements_v_10m") 
case class Loc(lat: Double, lon: Double) 
case class Wind(value: String, loc: Loc) 
val dataPoints = COSMODE_Wind.map{s => Wind(s.getDouble(2).toString, Loc( s.getDouble(0), s.getDouble(1)))} 
val dataPointsJson = dataPoints.toJSON.take(12) 
dataPointsJson.foreach(r => z.angularBind("locations", r)) 

I can see in the output that the values are retrieved:

dataPointsJson: Array[String] = Array({"value":"-1.6847639","loc":{"lat":53.3,"lon":6.8250003}}, {"value":"-1.7540998","loc":{"lat":53.3,"lon":6.875}}, {"value":"-1.7008772","loc":{"lat":53.3,"lon":6.925}}, {"value":"-1.6461897","loc":{"lat":53.3,"lon":6.975}}, {"value":"-1.8141584","loc":{"lat":53.3,"lon":7.025}}, {"value":"-1.9616194","loc":{"lat":53.3,"lon":7.0750003}}, {"value":"-2.5119123","loc":{"lat":53.3,"lon":7.125}}, {"value":"-2.8263655","loc":{"lat":53.3,"lon":7.175}}, {"value":"-2.54951","loc":{"lat":53.3,"lon":7.225}}, {"value":"-1.3688459","loc":{"lat":53.3,"lon":7.8}}, {"value":"-1.3200178","loc":{"lat":53.3,"lon":7.85}}, {"value":"-1.115428","loc":{"lat":53.3,"lon":7.9}})

And finally this is the Angular code where these values are processed:

var geoMarkers = L.layerGroup().addTo(map); 
var el = angular.element($('#map').parent('.ng-scope')); 
angular.element(el).ready(function() { 
window.locationWatcher = el.scope().compiledScope.$watch('locations', function(newValue, oldValue) { 
// geoMarkers.clearLayers(); -- if you want to only show new data clear the layer first 
   angular.forEach(newValue, function(wind) { 
	var marker = L.marker([ wind.loc.lat, wind.loc.lon ]) .bindPopup('wind.value').addTo(geoMarkers); 
		}); 
	}) 
}); 

I am getting nothing on the map.

When I debug the "wind" argument I can see the same value as in the output of the spark paragraph, but when I print the wind.loc or wind.loc.lat or wind.value I get an "undefined" I guess the json string is not conformed or I am doing something wrong reading the array.

Any help will be highly appreciated.

Kind regards, Paul

1 REPLY 1

Re: Bind leaflet map with angular and scala in Apache Zeppelin

Hi everyone,

I already found a solution. I was sending just strings to the angular controller (not a json object). In the controller an angular.Foreach is used to iterate the incoming value “newValue”. This function iterates both objects and arrays. What I sent was interpreted as an element of a strings array and not as Json object, i.e.

{"value":"-2.5119123","loc":{"lat":53.3,"lon":7.125}}

In the first iteration the value evaluated was:

"value":"-2.5119123"

And in the second:

"loc":{"lat":53.3,"lon":7.125}

I just modified the scala code in order to send the whole Array[String]:

%spark2
import scala.util.parsing.json.JSONObject
import org.apache.spark.sql._
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._
val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc)
val hadoopConf = sc.hadoopConfiguration
hadoopConf.set("mapreduce.input.fileinputformat.input.dir.recursive", "true")
val COSMODE_Wind = sqlContext.sql("SELECT lat0, long0, value FROM dev_sdsp.cosmode_single_level_elements_v_10m limit 100")
case class Loc(lat: Double, lon: Double)
case class Wind(value: String, loc: Loc)
val dataPoints = COSMODE_Wind.map{s => Wind(s.getDouble(2).toString, Loc( s.getDouble(0), s.getDouble(1)))}
val dataPointsJson = dataPoints.toJSON.take(100)
z.angularBind("locations", dataPointsJson)

Then the angular.Foreach iterate the whole “row”

{"value":"-2.5119123","loc":{"lat":53.3,"lon":7.125}}

Wich can be converted to a json object using javascript

Finally, the values are accessible using dot notation. Here the Angular snippet:

var el = angular.element($('#map').parent('.ng-scope'));
    angular.element(el).ready(function() {
        window.locationWatcher = el.scope().compiledScope.$watch('locations', function(newValue, oldValue) {
            // geoMarkers.clearLayers(); -- if you want to only show new data clear the layer first
            console.log("new value: " + newValue);
            angular.forEach(newValue, function(wind) {
                try { JSON.parse(wind); } catch(error) { alert(error); }
                windJSON = JSON.parse(wind)
                var marker = L.marker([windJSON.loc.lat, windJSON.loc.lon]).bindPopup(windJSON.value).addTo(geoMarkers);
            });
        })
    });

Hope ths helps someone.