├── DESCRIPTION ├── man ├── lpoints.Rd └── lmap.Rd ├── R └── rcl.R └── inst └── javascript └── rcl.js /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rcleaflet 2 | Title: Interactive maps in RCloud based on the Leaflet package 3 | Version: 1.0-0 4 | Author: Simon Urbanek 5 | Maintainer: Simon Urbanek 6 | Description: Interactive maps in RCloud based on the Leaflet package 7 | Imports: rcloud.support 8 | License: MIT 9 | -------------------------------------------------------------------------------- /man/lpoints.Rd: -------------------------------------------------------------------------------- 1 | \name{lpoints} 2 | \alias{lpoints} 3 | \title{ 4 | Add objects to an interactive Leaflet map 5 | } 6 | \description{ 7 | \code{lpoints} adds points (circles) to an interactive leaflet map. 8 | } 9 | \usage{ 10 | lpoints(lat, lon, col = "black", bg = "transparent", cex = 1, lwd = 1, ..., map = .cache$last.map) 11 | } 12 | %- maybe also 'usage' for other objects documented here. 13 | \arguments{ 14 | \item{lat}{numeric vector of latitudes} 15 | \item{lon}{numeric vector of longitudes} 16 | \item{col}{color (border) of the points} 17 | \item{bg}{background (fill) of the points} 18 | \item{cex}{expansion of the points} 19 | \item{lwd}{line width of the border} 20 | \item{\dots}{further arguments for future use} 21 | \item{map}{object of the class \code{RCloudLeaflet} representing the 22 | map to add to (as returned by \code{\link{lmap}}} 23 | } 24 | \details{ 25 | \code{lpoints} adds points to the map. Although the syntax is 26 | mimicking the well-known \code{points} method, is always behaves as if 27 | `pch=21` was specified. All arguments can be vectors and will be 28 | re-cycled accordingly. 29 | } 30 | \value{ 31 | Returns the \code{map} obejct. 32 | } 33 | \author{ 34 | Simon Urbanek 35 | } 36 | \seealso{ 37 | \code{\link{lmap}} 38 | } 39 | %\examples{ 40 | %} 41 | \keyword{aplot} 42 | -------------------------------------------------------------------------------- /man/lmap.Rd: -------------------------------------------------------------------------------- 1 | \name{lmap} 2 | \alias{lmap} 3 | \title{ 4 | Create an interactive map 5 | } 6 | \description{ 7 | \code{lmap} creates an instance of an interactive map using Leaflet. 8 | } 9 | \usage{ 10 | lmap(lat, lon, zoom = 10, where, width = 800, height = 600) 11 | } 12 | \arguments{ 13 | \item{lat}{latitude of the center} 14 | \item{lon}{longitude of the center} 15 | \item{zoom}{zoom level} 16 | \item{where}{optional, string identifying the element which will 17 | contain the map using jQuery selector notation. If absent, a new 18 | \code{
} element is created using \code{\link{rcloud.html.out}}.} 19 | \item{width}{if \code{where} is missing, the width (in pixels) of the 20 | new map element} 21 | \item{height}{if \code{where} is missing, the height (in pixels) of the 22 | new map element} 23 | } 24 | \details{ 25 | \code{lmap} creates a new instance of a Leaflet intercative map. It 26 | also sets the current active map to this objects such that calls to 27 | \code{\link{lpoints}} and other plotting functions will default to 28 | this instance if no map is specified explicitly. 29 | } 30 | \value{ 31 | Returns an object of the class \code{RCloudLeaflet} representing the 32 | map. 33 | } 34 | \author{ 35 | Simon Urbanek 36 | } 37 | \seealso{ 38 | \code{\link{lpoints}} 39 | } 40 | %\examples{ 41 | %} 42 | \keyword{hplot} 43 | -------------------------------------------------------------------------------- /R/rcl.R: -------------------------------------------------------------------------------- 1 | .cache <- new.env(FALSE, emptyenv()) 2 | 3 | lmap <- function(x=NULL,y=NULL,zoom=NULL,where=NULL,xlim=NULL,ylim=NULL, 4 | width=800, height=600,eventfunc=NULL,lat=y,lon=x) { 5 | if (missing(where)) { 6 | where <- paste0("rc_map_", as.integer(runif(1)*1e6)) 7 | rcloud.html.out(paste0("
")) 9 | where <- paste0("#", where) 10 | } 11 | if (is.null(.cache$ocaps)) { 12 | x <- paste(readLines(system.file("javascript", "rcl.js", 13 | package="rcleaflet"), 14 | warn=FALSE), collapse='\n') 15 | .cache$ocaps <- rcloud.install.js.module("rcleaflet", x, TRUE) 16 | } 17 | 18 | map <- structure(list(div=.cache$ocaps$map(where,lat,lon,zoom, 19 | xlim,ylim,eventfunc)), 20 | class="RCloudLeaflet") 21 | 22 | .cache$last.map <- map 23 | #return(map) 24 | } 25 | 26 | lremove <- function(map = .cache$last.map) { 27 | .cache$ocaps$remove(map$div) 28 | rm("last.map", envir = .cache) 29 | } 30 | 31 | lpanTo <- function(lat, lon, map = .cache$last.map) { 32 | .cache$ocaps$panTo(map$div, lat, lon) 33 | } 34 | 35 | ## map R colors to RGB space, also re-cycle as needed and split off RGB and A 36 | .mapColor <- function(col, n) { 37 | cc <- col2rgb(col, TRUE) 38 | 39 | l <- list(col=substr(rgb(cc[1,], cc[2,], cc[3,], cc[4,],, 255), 1, 7), 40 | alpha=as.vector(cc[4,])/255) 41 | if (length(l$col) > 1 && length(l$col) != n) { 42 | l$col <- rep(l$col, length.out=n) 43 | l$alpha <- rep(l$alpha, length.out=n) 44 | } 45 | l 46 | } 47 | 48 | lpoints <- function(x,y,col="black", bg="transparent", cex=1, lwd=1, 49 | popup=NULL,eventfunc=NULL,lat=y,lon=x, 50 | map=.cache$last.map) { 51 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 52 | ls <- c(length(lat), length(lon)) 53 | if (diff(ls)) { ## recycle 54 | lat <- rep(lat, length.out=max(ls)) 55 | lon <- rep(lon, length.out=max(ls)) 56 | } 57 | ## this is somewhat arbitrary - mapping between leaflet radius and cex ... 58 | cex <- cex * 72 59 | col <- .mapColor(col, ls[1]) 60 | bg <- .mapColor(bg, ls[1]) 61 | if (length(cex) > 1 && length(cex) != max(ls)){ 62 | cex <- rep(cex, length.out=max(ls)) 63 | } 64 | 65 | if (length(lwd) > 1 && length(lwd) != max(ls)){ 66 | lwd <- rep(lwd, length.out=max(ls)) 67 | } 68 | 69 | .cache$ocaps$points(map$div, lat, lon, col$col, bg$col, col$alpha, 70 | bg$alpha, cex, lwd, popup, eventfunc) 71 | invisible(map) 72 | } 73 | 74 | # An R lty has to become an SVG stroke-dasharray 75 | # This is going to be imperfect (to say the least) 76 | devLtyToSVG <- function(lty, lwd) { 77 | # Convert lty to numeric vec 78 | numlty <- switch(lty, 79 | solid = 0, 80 | # These numbers taken from ?par 81 | dashed = c(4, 4), 82 | dotted = c(1, 3), 83 | dotdash = c(1, 3, 4, 3), 84 | longdash = c(7, 3), 85 | twodash = c(2, 2, 6, 2), 86 | # Otherwise we're a hex string 87 | as.numeric(as.hexmode(strsplit(lty, "")[[1]]))) 88 | # Scale by lwd 89 | scaledlty <- numlty * lwd 90 | 91 | # Convert to SVG stroke-dasharray string 92 | paste(ifelse(scaledlty == 0,"none",round(scaledlty, 2)), 93 | collapse=",") 94 | } 95 | 96 | lsegments <- function(x1,y1,x2,y2, col = "black",lty = 1, lwd = 1, 97 | lat1=y1,lon1=x1,lat2=y2,lon2=x2,map=.cache$last.map){ 98 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 99 | ls <- c(length(lat1), length(lon1), length(lat2), length(lon2)) 100 | 101 | if (sum(abs(diff(ls)))) { ## recycle 102 | lat1 <- rep(lat1, length.out=max(ls)) 103 | lon1 <- rep(lon1, length.out=max(ls)) 104 | lat2 <- rep(lat2, length.out=max(ls)) 105 | lon2 <- rep(lon2, length.out=max(ls)) 106 | } 107 | col <- .mapColor(col, 1) 108 | if (length(lty) > 1 && length(lty) != max(ls)){ 109 | lty <- rep(lty, length.out=max(ls)) 110 | } 111 | if (length(lwd) > 1 && length(lwd) != max(ls)){ 112 | lwd <- rep(lwd, length.out=max(ls)) 113 | } 114 | 115 | .cache$ocaps$segments(map$div, lat1, lon1, lat2, lon2, col$col, 116 | devLtyToSVG(lty, lwd), lwd) 117 | invisible(map) 118 | } 119 | 120 | 121 | 122 | lpolyline <- function(x,y, col = "black", lty = 1, lwd = 1, 123 | lat=y,lon=x,map=.cache$last.map){ 124 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 125 | ls <- c(length(lat), length(lon)) 126 | 127 | if (sum(abs(diff(ls)))) { ## recycle 128 | lat <- rep(lat, length.out=max(ls)) 129 | lon <- rep(lon, length.out=max(ls)) 130 | } 131 | col <- .mapColor(col, 1) 132 | if (length(lty) > 1 && length(lty) != max(ls)) lty <- rep(lty,length.out=max(ls)) 133 | if (length(lwd) > 1 && length(lwd) != max(ls)) lwd <- rep(lwd,length.out=max(ls)) 134 | 135 | .cache$ocaps$polyline(map$div, lat, lon, col$col, 136 | devLtyToSVG(lty, lwd), lwd) 137 | invisible(map) 138 | } 139 | 140 | lmarkers <- function(x, y, popup=NULL, iconurl=NULL,eventfunc=NULL, 141 | lat=y, lon=x,map=.cache$last.map) { 142 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 143 | ls <- c(length(lat), length(lon)) 144 | if (diff(ls)) { ## recycle 145 | lat <- rep(lat, length.out=max(ls)) 146 | lon <- rep(lon, length.out=max(ls)) 147 | } 148 | .cache$ocaps$markers(map$div, lat, lon, popup, iconurl, eventfunc) 149 | invisible(map) 150 | } 151 | 152 | lpolygon <- function(x, y, popup=NULL, color='red', fillColor=color, 153 | weight=5, lat=y, lon=x,map=.cache$last.map) { 154 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 155 | ls <- c(length(lat), length(lon)) 156 | 157 | if (diff(ls)) { ## recycle 158 | lat <- rep(lat, length.out=max(ls)) 159 | lon <- rep(lon, length.out=max(ls)) 160 | } 161 | 162 | col <- .mapColor(color, 1) 163 | fill <- .mapColor(fillColor, 1) 164 | .cache$ocaps$polygon(map$div, lat, lon, popup, col$col, col$alpha, 165 | fill$col, fill$alpha, weight) 166 | invisible(map) 167 | } 168 | 169 | #Animations 170 | lanimatedPolyline <- function(x,y,durations,maxpts=0, 171 | stepsize=33,delay=0, col="blue", 172 | lty=1, lwd=1, lat=y,lon=x, 173 | map=.cache$last.map){ 174 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 175 | 176 | .cache$ocaps$animatedPolyline(map$div,lat,lon,durations, maxpts, 177 | stepsize,delay,col,devLtyToSVG(lty, lwd), 178 | lwd) 179 | invisible(map) 180 | } 181 | 182 | lanimatedMarker <- function(x,y,durations,stepsize=33,delay=0, 183 | lat=y,lon=x,map=.cache$last.map){ 184 | if (is.null(map$div)) stop("invalid map object - not a Leaflet map") 185 | 186 | .cache$ocaps$animatedMarker(map$div,lat,lon,durations,stepsize,delay) 187 | invisible(map) 188 | } 189 | 190 | getCurrentView <- function(map=.cache$last.map){ 191 | ret <-.cache$ocaps$getCurrentView(map$div) 192 | return(list(xlim=unlist(ret$xlim),ylim=unlist(ret$ylim),zoom=ret$zoom)) 193 | } 194 | -------------------------------------------------------------------------------- /inst/javascript/rcl.js: -------------------------------------------------------------------------------- 1 | /*global $ L require */ 2 | 3 | // Load CSS and JS 4 | function loadCss(filename){ 5 | var fileref=document.createElement("link"); 6 | fileref.setAttribute("rel", "stylesheet"); 7 | fileref.setAttribute("type", "text/css"); 8 | fileref.setAttribute("href", filename); 9 | document.getElementsByTagName( "head" )[0].appendChild( fileref ); 10 | } 11 | 12 | 13 | require.config({ 14 | paths: { 15 | leaflet: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet' 16 | }, 17 | shim: { 18 | leaflet: { 19 | exports: 'L' 20 | } 21 | } 22 | }); 23 | 24 | function initMap(L, div, lat, lon, zoom, xlim, ylim, eventfunc, k) { 25 | loadCss('https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.css'); 26 | $(div).resizable({stop: function() { map.invalidateSize(); }}); 27 | var map = L.map($(div)[0]); 28 | 29 | if(xlim,ylim){ //fit to bbox 30 | map.fitBounds([[ylim[0],xlim[0]],[ylim[1],xlim[1]]]); 31 | } 32 | else{ //zoom to lat,lon 33 | map.setView([lat,lon], zoom); 34 | } 35 | 36 | //Register callbacks 37 | for(var e in eventfunc){ 38 | map.on(e, makeEventFunc(eventfunc[e],map)); 39 | } 40 | 41 | //L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png') 42 | L.tileLayer('http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png') 43 | .addTo(map); 44 | 45 | if (!window.rcleaflet) window.rcleaflet = {}; 46 | window.rcleaflet[div] = { 47 | 'L':L, 'map':map, 'points':[], 'polylines':[], 48 | 'segments':[], 'polygons': [], 'markers':[] 49 | }; 50 | 51 | k(null, div); 52 | } 53 | 54 | function _genSteps (lat, lon, durations, stepsize) { 55 | var allsteps = []; 56 | for(var i=0; i< lat.length-1; i++){ 57 | var s = Math.ceil(durations[i]/stepsize); 58 | s = Math.max(s,1); 59 | var d = L.latLng(lat[i+1]-lat[i],lon[i+1]-lon[i]); 60 | d = L.latLng(d.lat/s,d.lng/s); 61 | var steps = new Array(s); 62 | for(var j=0; j < s-1; j++){ 63 | steps[j] = L.latLng(lat[i]+d.lat*(j+1),lon[i]+d.lng*(j+1)); 64 | } 65 | steps[s-1] = L.latLng(lat[i+1],lon[i+1]); 66 | allsteps[i] = steps; 67 | } 68 | return allsteps; 69 | } 70 | 71 | function makeEventFunc(f,obj) { 72 | return function(){ 73 | f(function(err,res){ 74 | console.log('complete callback',obj,err,res); 75 | }); 76 | }; 77 | }; 78 | 79 | (function(){ 80 | return { 81 | map:function(div, lat, lon, zoom, xlim, ylim, eventfunc, k){ 82 | // this serves as an init function, 83 | // it works because it really blocks 84 | try{ 85 | var L = window.rcleaflet[div].L; 86 | initMap(L,div,lat,lon,zoom,xlim,ylim,eventfunc,k); 87 | } 88 | catch(e){ // Load Leaflet 89 | require(['leaflet'],function(L){ 90 | initMap(L,div,lat,lon,zoom,xlim,ylim,eventfunc,k); 91 | }); 92 | } 93 | }, 94 | 95 | getCurrentView: function(div, k){ 96 | var L = window.rcleaflet[div].L; 97 | var map = window.rcleaflet[div].map; 98 | var b = map.getBounds(); 99 | var z = map.getZoom(); 100 | var ne = b.getNorthEast(); 101 | var sw = b.getSouthWest(); 102 | 103 | k(null, { 104 | zoom: z, 105 | ylim:[sw.lat,ne.lat], 106 | xlim:[sw.lng,ne.lng] 107 | }); 108 | }, 109 | 110 | remove: function(div, k) { 111 | var map = window.rcleaflet[div].map; 112 | map.remove(); 113 | k(null, true); 114 | }, 115 | 116 | panTo: function(div, lat, lon, k) { 117 | var map = window.rcleaflet[div].map; 118 | var L = window.rcleaflet[div].L; 119 | map.panTo(new L.LatLng(lat, lon)); 120 | k(null, true); 121 | }, 122 | 123 | removePolygons: function(div,k){ //temp function 124 | var L = window.rcleaflet[div].L; 125 | var map = window.rcleaflet[div].map; 126 | 127 | window.rcleaflet[div].polygons.forEach(function(d){ 128 | map.removeLayer(d); 129 | }); 130 | k(null,true); 131 | }, 132 | 133 | points:function(div, lat, lon, col, fill, colA, fillA, rad, lwd, 134 | popup, eventfunc, k) { 135 | var L = window.rcleaflet[div].L; 136 | var map = window.rcleaflet[div].map; 137 | if (!lat.length) { 138 | lat = [lat]; 139 | lon = [lon]; 140 | popup = [popup]; 141 | } 142 | 143 | for (var i = 0; i < lat.length; i++){ 144 | var opts = { 145 | color: col.charAt ? col : col[i], 146 | fillColor: fill.charAt ? fill : fill[i], 147 | fillOpacity: fillA.length ? fillA[i] : fillA, 148 | opacity: colA.length ? colA[i] : colA, 149 | weight: lwd.length ? lwd[i] : lwd 150 | }; 151 | var r = rad.length? rad[i]:rad; 152 | var c = L.circle([lat[i], lon[i]], r , opts); 153 | 154 | if (popup && popup[i]){ 155 | c.bindPopup(popup[i]); 156 | } 157 | 158 | //Register callbacks 159 | for(var e in eventfunc){ 160 | map.on(e, makeEventFunc(eventfunc[e],c)); 161 | } 162 | c.addTo(map); 163 | window.rcleaflet[div].points.push(c); 164 | } 165 | 166 | k(null, true); 167 | }, 168 | 169 | segments:function(div, lat1, lon1, lat2, lon2, col, lty, lwd, k) { 170 | var L = window.rcleaflet[div].L; 171 | var map = window.rcleaflet[div].map; 172 | 173 | if(!lat1.length && !lon1.length && 174 | !lat2.length && !lon2.length && !lwd.length){ 175 | //special case for a single element 176 | lat1 = [lat1]; 177 | lon1 = [lon1]; 178 | lat2 = [lat2]; 179 | lon2 = [lon2]; 180 | lwd = [lwd]; 181 | } 182 | for(var i=0; i < lat1.length; i++){ 183 | var opts={ 184 | color: col.charAt ? col : col[i], 185 | dashArray: lty.charAt ? lty : lty[i], 186 | weight: lwd.length ? lwd[i] : lwd 187 | }; 188 | var pl = L.polyline([[lat1[i],lon1[i]], 189 | [lat2[i],lon2[i]]],opts); 190 | pl.addTo(map); 191 | window.rcleaflet[div].polylines.push(pl); 192 | } 193 | k(null, true); 194 | }, 195 | 196 | markers:function(div, lat, lon, popup,iconurl, 197 | eventfunc,k) { 198 | var L = window.rcleaflet[div].L; 199 | var map = window.rcleaflet[div].map; 200 | 201 | if (!lat.length){ //make them arrays 202 | lat = [lat]; 203 | lon = [lon]; 204 | } 205 | 206 | if(popup && !Array.isArray(popup)){ 207 | popup=Array.apply(null,Array(lat.length)).map(function(d){ 208 | return popup; 209 | }); 210 | } 211 | 212 | if(iconurl && !Array.isArray(iconurl)){ 213 | iconurl=Array.apply(null,Array(lat.length)).map(function(d){ 214 | return iconurl; 215 | }); 216 | } 217 | 218 | for (var i = 0; i < lat.length; i++){ 219 | var myicon= new L.Icon.Default(); 220 | if(iconurl && iconurl[i]){ 221 | myicon=L.icon({iconUrl:iconurl[i]}); 222 | } 223 | 224 | var m = L.marker([lat[i], lon[i]],{icon:myicon}); 225 | 226 | if (popup && popup[i]){ 227 | m.bindPopup(popup[i]); 228 | } 229 | 230 | //Register callbacks 231 | for(var e in eventfunc){ 232 | m.on(e, makeEventFunc(eventfunc[e],m)); 233 | } 234 | m.addTo(map); 235 | window.rcleaflet[div].markers.push(m); 236 | } 237 | 238 | k(null, true); 239 | }, 240 | 241 | polyline:function(div, lat, lon, col, lty, lwd,k) { 242 | var L = window.rcleaflet[div].L; 243 | var map = window.rcleaflet[div].map; 244 | var points = []; 245 | 246 | for(var i = 0; i < lat.length; i++){ 247 | points.push(L.latLng(lat[i], lon[i])); 248 | } 249 | var opts={ 250 | color: col.charAt ? col : col[i], 251 | dashArray: lty.charAt ? lty : lty[i], 252 | weight: lwd.length ? lwd[i] : lwd 253 | }; 254 | 255 | var pl = L.polyline(points, opts); 256 | pl.addTo(map); 257 | window.rcleaflet[div].polylines.push(pl); 258 | 259 | k(null, true); 260 | }, 261 | 262 | polygon:function(div, lat, lon, popup, color, opacity, 263 | fillColor, fillOpacity, weight, k) { 264 | var L = window.rcleaflet[div].L; 265 | var map = window.rcleaflet[div].map; 266 | var boundaries = []; 267 | var points = []; 268 | 269 | for(var i = 0; i < lat.length; i++){ 270 | if (!isNaN(lat[i])){ 271 | points.push(L.latLng(lat[i], lon[i])); 272 | } 273 | else{ 274 | var dir = points.reduce(function(p,c,i,arr){ 275 | if (i >= arr.length-1){ // 276 | return p; 277 | } 278 | 279 | return p + (arr[i+1].lng - arr[i].lng )* 280 | (arr[i+1].lat+arr[i].lat); 281 | },0); 282 | 283 | points.pop(); 284 | if (dir >= 0){ //clockwise 285 | boundaries.push([points.slice()]); 286 | } 287 | else{ //counter clockwise (hole) 288 | boundaries[boundaries.length-1].push(points.slice()); 289 | } 290 | points = []; 291 | } 292 | } 293 | 294 | //process the last set of points 295 | dir = points.reduce(function(p,c,i,arr){ 296 | if (i >= arr.length-1){ // 297 | return p; 298 | } 299 | return p + (arr[i+1].lng - arr[i].lng )* 300 | (arr[i+1].lat + arr[i].lat); 301 | },0); 302 | 303 | points.pop(); 304 | if (dir >= 0){ //clockwise 305 | boundaries.push([points.slice()]); 306 | } 307 | else{ //counter clockwise (hole) 308 | boundaries[boundaries.length-1].push(points.slice()); 309 | } 310 | 311 | var mp=L.multiPolygon(boundaries, {color: color, 312 | opacity: opacity, 313 | fillColor: fillColor, 314 | fillOpacity: fillOpacity, 315 | weight: weight }); 316 | if(popup){ 317 | mp.bindPopup(popup); 318 | } 319 | mp.addTo(map); 320 | window.rcleaflet[div].polygons.push(mp); 321 | 322 | k(null, true); 323 | }, 324 | 325 | animatedPolyline: function(div,lat,lon,durations,maxpts, 326 | stepsize,delay,col,lty,lwd,k){ 327 | stepsize = stepsize || 1000.0/30; 328 | delay = delay || 1000; 329 | 330 | var L = window.rcleaflet[div].L; 331 | var map = window.rcleaflet[div].map; 332 | 333 | var i=0; 334 | var opts={ 335 | color: col.charAt ? col : col[i], 336 | dashArray: lty.charAt ? lty : lty[i], 337 | weight: lwd.length ? lwd[i] : lwd 338 | }; 339 | 340 | var pl = L.polyline([[lat[0],lon[0]]],opts); 341 | pl.addTo(map); 342 | 343 | var s = _genSteps(lat,lon,durations,stepsize); 344 | 345 | var start = window.performance.now(); 346 | var ll = pl.getLatLngs(); 347 | var lastp = ll[ll.length-1]; 348 | ll.push(lastp); 349 | pl.setLatLngs(ll); //dup the last point 350 | var op = window.performance.now()-start; 351 | 352 | //estimate the required time for setting the polyline 353 | stepsize -= op; 354 | stepsize = Math.max(stepsize,0); 355 | console.log(stepsize); 356 | 357 | window.setTimeout(function(){ 358 | var timer = window.setInterval(function(){ 359 | var ll = pl.getLatLngs(); 360 | ll[ll.length-1]=s[0].shift(); 361 | 362 | if (s[0].length < 1) { //last pt of the current interval 363 | ll.push(ll[ll.length-1]); //repeat the last pt; 364 | s.shift(); //remove the empty list 365 | } 366 | //set the new latlng 367 | if(maxpts >= 1){ 368 | ll = ll.slice(-maxpts); 369 | } 370 | pl.setLatLngs(ll); 371 | 372 | if (s.length < 1){ 373 | window.clearInterval(timer); 374 | ll = pl.getLatLngs(); 375 | ll.pop(); 376 | pl.setLatLngs(ll); 377 | console.log(pl.getLatLngs()); 378 | } 379 | },stepsize); 380 | },delay); 381 | k(null, true); 382 | }, 383 | 384 | animatedMarker: function (div,lat, lon, durations,stepsize,delay,k){ 385 | stepsize = stepsize || 1000.0/30; 386 | delay = delay || 1000; 387 | 388 | var L = window.rcleaflet[div].L; 389 | var map = window.rcleaflet[div].map; 390 | var m = L.marker([lat[0],lon[0]]); 391 | m.addTo(map); 392 | 393 | var s = _genSteps(lat,lon,durations,stepsize); 394 | 395 | var start = window.performance.now(); 396 | var ll = m.getLatLng(); 397 | m.setLatLng(ll); //set position 398 | var op = window.performance.now()-start; 399 | 400 | //estimate the required time for setting the marker 401 | stepsize -= op; 402 | stepsize = Math.max(stepsize,0); 403 | console.log(stepsize); 404 | 405 | window.setTimeout(function(){ 406 | var timer = window.setInterval(function(){ 407 | m.setLatLng(s[0].shift()); 408 | 409 | if (s[0].length < 1) { //last pt of the current interval 410 | s.shift(); //remove the empty list 411 | } 412 | if (s.length < 1){ 413 | window.clearInterval(timer); 414 | } 415 | },stepsize); 416 | },delay); 417 | k(null, true); 418 | } 419 | }; 420 | })(); 421 | --------------------------------------------------------------------------------