helgasoft / echarty

Minimal R/Shiny Interface to ECharts.js
https://helgasoft.github.io/echarty/
88 stars 3 forks source link

map with shapefile polylines or polygons #10

Closed helgasoft closed 1 year ago

helgasoft commented 2 years ago

Proof of concept - Leaflet map with shapefile polylines. Idea from @Robinlovelace. We prefer Leaflet map as most versatile. Other options are 'bmap' and gmap, which are based on Baidu and Google and require an API key.

destfile <- tempfile('shape')
download.file('https://apa.ny.gov/gis/GisData/Boundaries/AdirondackParkBoundary2017.zip', 
                  destfile, mode='wb', method='curl')
unzip(destfile, exdir='unzipped')  # new unzipped folder under getwd()
# convert shape coords to lat/lng
library(rgdal)
ogr <- readOGR(dsn= 'unzipped', layer= 'AdirondackParkBoundary2017')
tmp <- sp::spTransform(ogr, sp::CRS("+init=epsg:4326"))
# convert lat/lng to ECharts format in dt
dt <- list()
for(i in 1:length(tmp@lines)) {
    df <- as.data.frame(tmp@lines[[i]]@Lines[[1]]@coords)  # Adirondack
    coords <- list()
    for(k in 1:nrow(df)) coords <- append(coords, list(as.numeric(df[k,])))
    dt <- append(dt, list(list(name= paste0('L',i), coords= coords)))
}

library(echarty)
p <- ec.init(load= 'leaflet')
p$x$opts$leaflet <- list(
    zoom= 8, roam= TRUE, center= unlist(dt[[1]]$coords[1]))
p$x$opts$series <- list(
    list(type= 'lines', coordinateSystem= 'leaflet', polyline= TRUE,
        lineStyle= list(width=3), color= 'red', 
        #progressiveThreshold= 500, progressive= 200,
        data= dt
    )
    ,list(type= 'lines', coordinateSystem= 'leaflet', polyline= TRUE,
        lineStyle= list(width=0), color= 'blue', zlevel= 1,
        data= dt,
        effect= list(show= TRUE, constantSpeed= 20, trailLength= 0.1, symbolSize= 3)
    )
)
p$x$opts$tooltip <- list(show=TRUE)
p 

ezgif com-gif-maker

If you like this solution, please consider granting a Github star ⭐ to echarty.

Robinlovelace commented 2 years ago

This looks good, many thanks @helgasoft! Do you have a working version starting with an sf object? sf supersedes sp so support for it would make this solution much more future-proof. Good news: switch from sp to sf should be easy and I can provide pointers if you need them.

helgasoft commented 2 years ago

Here is one way of getting the data through sf.

library(sf)
fname <- system.file("shape/nc.shp", package="sf")
nc <- st_read(fname) 
dats <- list()
for(i in 1:nrow(nc)) {
    geom <- as.data.frame(nc$geometry[[i]][[1]][[1]])
    coords <- list()
    for(k in 1:nrow(geom)) 
        coords <- append(coords, list(c(geom$V1[k], geom$V2[k])))
    dats <- append(dats, list(list(name= nc$NAME[i], coords=coords)))
}
centr <- as.data.frame(nc$geometry[[1]][[1]][[1]])

library(echarty)
p <- ec.init(load='leaflet')
p$x$opts$series <- list(
    list(type='lines', coordinateSystem='leaflet', polyline= TRUE,
          lineStyle= list(width=3), color= 'red', name= 'counties',
          data= dats
    ))
p$x$opts$tooltip <- list(show= TRUE)
p$x$opts$legend = list(show= TRUE)
p$x$opts$leaflet$center <- c(mean(centr[,1]), mean(centr[,2]))
p$x$opts$leaflet$zoom <- 6
p

image

helgasoft commented 2 years ago

Previous example is with polylines. This one - with filled polygons. Tooltips show each polygon's name which is a county name from the sf shape data.

library(sf)
fname <- system.file("shape/nc.shp", package="sf")
nc <- st_read(fname) 
cntis <- list()  # counties
for(i in 1:nrow(nc)) {
    geom <- as.data.frame(nc$geometry[[i]][[1]][[1]])
    coords <- list()
    for(k in 1:nrow(geom)) 
        coords <- append(coords, list(c(geom$V1[k], geom$V2[k])))
    cntis <- append(cntis, list(list(
        type= 'custom', coordinateSystem= 'leaflet', 
        name= nc$NAME[i],   # county name for tooltip
        renderItem= htmlwidgets::JS('riPolygon'),
        itemStyle= list(opacity= 0.3),
        tooltip= list(formatter= '{a}'),
        data= coords)))
}
centr <- as.data.frame(nc$geometry[[1]][[1]][[1]])

library(echarty)
p <- ec.init(load= c('leaflet', 'custom'), 
         series= cntis,
         tooltip= list(show= TRUE)
)
# update leaflet presets
p$x$opts$leaflet$center <- c(mean(centr[,1]), mean(centr[,2]))
p$x$opts$leaflet$zoom <- 6
p

image

Robinlovelace commented 2 years ago

Nice! Could there be a function to visualise it in a single command, e.g. ec.sf(nc)

helgasoft commented 2 years ago

Implemented in latest echarty v.1.5.6.02. Check new command ?ec.util. See also more code and demo clip.

library(sf); library(echarty)
fname <- system.file("shape/nc.shp", package="sf")
nc <- as.data.frame(st_read(fname))

ec.init(load= c('leaflet', 'custom'),  # always load custom for polygons
        js= ec.util(cmd='sf.bbox', bbox=st_bbox(nc$geometry)), 
        series= ec.util(df= nc, nid= 'NAME', itemStyle= list(opacity= 0.3), verbose=TRUE),
        tooltip= list(formatter= '{a}')
)

#  scatter points can have a common name set for legend, no need for _nid_ param
fn <- ec.util(cmd= 'sf.unzip', 
              url= 'https://mapcruzin.com/sierra-leone-shapefiles/points.zip')
nc <- as.data.frame(st_read(fn))

ec.init(load= c('leaflet'),
        js= ec.util(cmd= 'sf.bbox', bbox= st_bbox(nc$geometry)), 
        series= ec.util(df= nc, name= 'spots', itemStyle= list(color= 'red'), verbose=TRUE),
        tooltip= list(formatter= '{a}'), legend= list(show= TRUE)
)
helgasoft commented 1 year ago

leaflet with timeline, for @leovan

tmp <- '
lng,lat,name,date,place
-118.808101,32.843715,"Seabed","2021-02-02","location A"
-117.332678,34.845565,"Lancaster","2021-04-02","location A"
-116.127504,32.846118,"fwy #8","2021-04-02","place B"
-117.316886,30.961700,"Baja","2021-07-02","place B"'
df <- read.csv(text= tmp, header= TRUE)

library(echarty)
df |> dplyr::group_by(place) |> ec.init(
  load= 'leaflet',
  leaflet= list(layerControl= list(position= 'topright'),
    tiles= list(
      list(
        label= 'Open Street Map',
        urlTemplate= 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
        options= list(attribution= '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, Tiles courtesy of <a href="http://hot.openstreetmap.org/" target="_blank">Humanitarian OpenStreetMap Team</a>')
      ),
      list(
        label= 'Stamen',
        urlTemplate= 'https://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}{r}.{ext}',
        options= list(attribution= 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>',
                      subdomains= 'abcd',   maxZoom= 18, ext= 'png')
      )
    )
  ),
  tl.series= list(type= 'scatter', coordinateSystem= 'leaflet',
                  symbolSize= 15, 
                  encode= list(lng= 'lng', lat= 'lat', tooltip= c(3,4))),
  tooltip= list(show= T), color= 'red'
)

image if you like this solution, please consider granting a Github star ⭐ to echarty.