Support Questions

Find answers, ask questions, and share your expertise
Announcements
Check out our newest addition to the community, the Cloudera Data Analytics (CDA) group hub.

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

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.

Take a Tour of the Community
Don't have an account?
Your experience may be limited. Sign in to explore more.