(CC-BY-NC-SA)
This document was produced on Mo Mär 14 2016 using mapview
version 1.0.9
Currently you will find projView only in the development branch of mapview. If you want to use it the probably easiest way to install mapview
is to use library("devtools")
. With devtools
installed and loaded type the following:
For the development version:
install_github("environmentalinformatics-marburg/mapview", ref = "develop")
Why would you be interested to visualize projected data? The answer is two folded but simple. First there are areas of the earth which you cannot map with a pseudo-Mercator projection and second (at least for me) even more important the adequate or inadequate cartographic representation of spatial data has a deep impact on our visual reception and interpretation of this data. The straightforward conclusion is that there is a high need for projected mapping.
Beyond all mapping philosophy let’s focus on a practical problem. We want to analyze and visualize some recent research data from the antartic continent using R and slippy maps. There is no possibility to use leaflet maps beyond the -85 degree latitude due to the implemented projections and we have to switch over to the great Open Source Geographic Information System QGIS or another mature GIS software package. With no doubt we may also use the well known static plotting packages of R - but no slippy maps for the penguins and polar bears within R.
Leaflet is a fascinating JavaScript library which is not only lightweight and easy to use. but moreover fully extensible with plug-ins and own JS scripts. With respect to our needs the proj4leaflet extension of leaflet is dealing with projections. The R interface to JS-leaflet is provided by the leaflet package and in turn the mapview package provides a comfortable wrapper for the R leaflet package. It seems to be obvious that this chaining may lead to some restrictions and exactly this is the situation.
ProjView uses its own interface directly to leaflet. To keep it in line with leaflet and mapview it is also using the htmlwidget package. the main advantage is that there is no need to live with the restrictions of the outlined chain but on the other hand you have to to it by your own means. Therefore projView is not neat less integrated and its usage and functionality is slightly different and restricted.
As we already have learned, the mapview penguin story ends at 85 degree of latitude. Using projView continuous the story. Unfortunately first we need to invest in the business - we have to gather some informations.
You know what to do? Perfect. Search “NASA leaflet earthdata” and choose the appropriate GIBS Map Library Usage hit. Navigate to Leaflet and choose GIS Web examples. Find Antarctic (EPSG:3031) and move down in this tutorial to parameter format for use in preview.
Basically for slippy maps you need on-line data from service providers. Best known are the WMS and OSM tiling services. All of some send you by request rasterized tiles of georeferenced maps, satellite images and so on. Usually you want to add your own vector or raster data overlay. Depending on the service and the client there are almost all possibilities out there. One of leaflets major pros is that it is easy and fast. The main reason is that it is a client side approach what means that your browser and your CPU has to do all the work because usually you are getting the data “as it is delivered”.
To learn how to prepare on-line services for mapview/leaflet we have to dive into the world of web mapping services.You will find a brief and simple explanation of how web maps (web services for web maps) works at Mapbox in the article ( How web maps works ). Unfortunately you have to forget the last conclusion.
Basically we need to access from a high level client (mapview) data tiles to retrieve. So we have to to know (1) which products are available (2) which map projections are available (3) how many zoom levels per product are available and (4) which protocol to access the data is available.
Due to the fact that without a service provider you hardly will find a service and data, it is obviously rational that you first take a look at this topic. For an introduction of the WMS related services you will find a perfect outline of what OGC services are around and what they mean in the QGIS OGC Documentation. Please note even if this documentations refers to GIS as a client all decribed concepts of the services are valid for all type of clients. For the OSM tiling concept you may have a look at the OSM Wiki topic called Tiles. Both concepts differ pretty much even if you will get as a result - tiles.
In theory, everything seems to be clear so far.
We have learned that it makes sense to start at a data provider and to find out what standards products on which projection and zoomlevel they provide.
ProjView supports up to now WMTS related services.
A big shot and always a good first try in data retrieval processing and providing in the earth science scene is the NASA Earth Observing System Data and Information System EOSDIS. Don’t get confused. They provide an incredible amount of information and data but you have to know how to find it. It is a typical govermental structure. EOSDIS implements the Earth Science Data and Information System ESDIS for data providing. Within this EarthData portal you will find all types of interfaces and services. Best way to get real information is the usage of the EarthData-Wiki.
As we are interested in web served data we need providers of this data. Just to have an example we will have a look at the NASA Global Imagery Browse Services GIBS. Here you will find all kinds of data and informations you may interested in. The services implement functionality for automated anaysis of the provided Content. We will use the most common OGC Web Map Tile Service (WMTS). The EarthData-Wiki provides all information at GIBS API for Developers OGC Web Map Tile Service (WMTS).
As we are interested in the antarctic region we look for something like Antarctic polar stereographic - EPSG:3031 . We choose the REST GetCapabilities link and get an incredible XML output.
You can read it now manually or write a script to extract the information you need.
projView is using the proj4leaflet api. For a basic setup of tiles we need at least the following arguments.
Let’s assume we are interested in the Blue Marble Shaded Relief Bathymetry. If we search for this layer in the REST GetCapabilities XML file we will see something like:
<Layer>
<ows:Title>BlueMarble_ShadedRelief_Bathymetry</ows:Title>
<ows:LowerCorner>-180 -90</ows:LowerCorner>
<ows:UpperCorner>180 -38.941373</ows:UpperCorner>
</ows:WGS84BoundingBox>
<ows:BoundingBox crs="urn:ogc:def:crs:EPSG::3031">
<ows:LowerCorner>-4194304 -4194304</ows:LowerCorner>
<ows:UpperCorner>4194304 4194304</ows:UpperCorner>
</ows:BoundingBox>
<ows:Identifier>BlueMarble_ShadedRelief_Bathymetry</ows:Identifier>
<Style isDefault="true">
<ows:Title>default</ows:Title>
<ows:Identifier>default</ows:Identifier>
</Style>
<Format>image/jpeg</Format>
<TileMatrixSetLink>
<TileMatrixSet>EPSG3031_500m</TileMatrixSet>
</TileMatrixSetLink>
<ResourceURL format="image/jpeg" resourceType="tile" template="http://map1.vis.earthdata.nasa.gov/wmts-antarctic/BlueMarble_ShadedRelief_Bathymetry/default/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg"/>
</Layer>
Admittedly annoying to read but it provides all necessary information in a compact way.
First we have to analyze the primary arguments:
If we focus ResourceURL
we will find more information in the the argument template
:
http://map1.vis.earthdata.nasa.gov/wmts-antarctic/BlueMarble_ShadedRelief_Bathymetry/default/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpg
To construct a valid URL we also need the content of the following variables: * {TileMatrixSet}
* {TileMatrix}
,{TileRow}
,{TileCol}
There are still some parameters missing which are not documented in the xml file. We will find tileSize and zoomlevel information at the EPSG:3031 examples table. Additionally it is common that the variable {TileMatrix},{TileRow},{TileCol}
are corresponding to the subdomains of the WMTS Rest format (i.e. abc
).
Ready to compile the information.
Now we need an appropriate structure for these parameters to make it less complicated to feed the projView function with this information. This is a simple nested list. If we fill it with the values it looks like this:
NASA=list(service="OSM", # name of the list
L.tileLayer="https://map1{s}.vis.earthdata.nasa.gov/wmts-antarctic/", # base address <ResourceURL>
# layer list is structured like the path of the url in this case: <ows:Identifier>,<TileMatrixSet>
layer=list( "BlueMarble_ShadedRelief_Bathymetry" = list("BlueMarble_ShadedRelief_Bathymetry","default","EPSG3031_500m","{z}/{y}/{x}"),
"AMSR2_Sea_Ice_Brightness_Temp_6km_89H"= list("AMSR2_Sea_Ice_Brightness_Temp_6km_89H","default","2014-02-04","EPSG3031_1km","{z}/{y}/{x}"),
"MODIS_Terra_Snow_Cover" = list("MODIS_Terra_Snow_Cover","default","2014-02-04","EPSG3031_1km","{z}/{y}/{x}")
), # end of layer list
format="image/jpg",#<Format>
tileSize="512", # min resolution EPSG:3031 examples
subdomains="abc", # most often used -> can be assumed as default
noWrap ="true", # most often used -> can be assumed as default
# attribution has to meet the data providers needs and your personal interpretation of transparence
attribution="<a href='https://wiki.earthdata.nasa.gov/display/GIBS'> NASA EOSDIS GIBS</a> || <a href='https://github.com/kartena/Proj4Leaflet'> Proj4Leaflet</a> | Projection: <a href='http://spatialreference.org/ref/epsg/wgs-84-antarctic-polar-stereographic/'> EPSG3031</a>",
params=list(t_epsg="EPSG:3031", # exactly this the EPSG Code beside all other possibilieties and standards if possible keep it like this
t_srs="+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs", # corresponding proj4 strings best be taken from href='http://spatialreference.org
mapCenter=list(cLat="-90", #see example map EPSG:3031 examples
cLon="0"), #see example map EPSG:3031 examples
initialZoom="1", #EPSG:3031 examples: Please note that the "top tile" (i.e., zoom level 0) is not currently supported by GIBS.
zoomLevels="5", # zoom/levels EPSG:3031 examples
initialResolution="256", # = minresolution EPSG:3031 examples
ovlBounds=list(minx="-4194304", ## <ows:LowerCorner>
miny="-4194304", ## <ows:LowerCorner>
maxx="4194304", ## <ows:UpperCorner>
maxy="4194304"), ## <ows:UpperCorner>
origin=list(olx="-4194304", ## <ows:LowerCorner> firstvalue minx
oly="4194304"), ## <ows:UpperCorner> lastvalue maxy
relUrl="" ## obsolete used for local tiles
))
You may imagine that serving of tiles is a world itself. Therefore it seems to be pretty ambitious to find a all in one solution. What I tried so far is to get a as simple as possible way to use WMS and OSM services. You should know some internal calculations and undocumented argument usage to prevent that you are going mad watching the weird behaviour of the tool.
First a dump of the list to identfy the crucial arguments we have to talk about.
List of 11
$ service : chr "OSM"
$ L.tileLayer: chr "https://map1{s}.vis.earthdata.nasa.gov/wmts-antarctic/"
$ layer :List of 3
..$ BlueMarble_ShadedRelief_Bathymetry :List of 4
.. ..$ : chr "BlueMarble_ShadedRelief_Bathymetry"
.. ..$ : chr "default"
.. ..$ : chr "EPSG3031_500m"
.. ..$ : chr "{z}/{y}/{x}"
..$ AMSR2_Sea_Ice_Brightness_Temp_6km_89H:List of 5
.. ..$ : chr "AMSR2_Sea_Ice_Brightness_Temp_6km_89H"
.. ..$ : chr "default"
.. ..$ : chr "2014-02-04"
.. ..$ : chr "EPSG3031_1km"
.. ..$ : chr "{z}/{y}/{x}"
..$ MODIS_Terra_Snow_Cover :List of 5
.. ..$ : chr "MODIS_Terra_Snow_Cover"
.. ..$ : chr "default"
.. ..$ : chr "2014-02-04"
.. ..$ : chr "EPSG3031_1km"
.. ..$ : chr "{z}/{y}/{x}"
$ format : chr "image/jpg"
$ tileSize : chr "512"
$ subdomains : chr "abc"
$ minZoom : num 0
$ maxZoom : num 5
$ noWrap : chr "true"
$ attribution: chr "<a href='https://wiki.ear __truncated__'"
$ params :List of 9
..$ t_epsg : chr "EPSG:3031"
..$ t_srs : chr "+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs"
..$ mapCenter :List of 2
.. ..$ cLat: chr "-90"
.. ..$ cLon: chr "0"
..$ initialZoom : chr "0"
..$ zoomLevels : chr "5"
..$ initialResolution: chr "256"
..$ ovlBounds :List of 4
.. ..$ minx: chr "-4194304"
.. ..$ miny: chr "-4194304"
.. ..$ maxx: chr "4194304"
.. ..$ maxy: chr "4194304"
..$ origin :List of 2
.. ..$ olx: chr "-4194304"
.. ..$ oly: chr "4194304"
..$ relUrl : chr ""
The whole List will be iterated using the sublist $layer
to build the URL, in the example case 3 times. The root of the url is always $L.tileLayer
:
Note the first item will be always the baselayer of the map and can not be switched on an off.
Each layer item is then iterated over the path list. Note the items must be in the sequence of the path because they were pasted in exactly one after the other.
As a result you should get the (correct) URL for the service.
All items except $params
and $layer
are parsed and used to set up a correct L.tileLayer{}
parameter list. They can be crucial and it is possible that you have to play around a bit
All Parameters of the $param
list belong to the leaflet map object respective the Proj4Leaflet extension for non leaflet CRSs.
Most of them are as they are looking like. Especially $initialResolution
, $ovlBound
and $origin
. These parameter set is ruling the leaflet map object setup and even if everything all other arguments are perfect, you will get strange or no effects, if they don’t meet the needs of the data providers.
Usually it is tried to calculate the resolution automatically. the approach is simple:
maxResolution <- res / div
resolution<- list()
for ( i in seq(0,maxZoom)){
resolution[i+1] <- maxResolution / 2^i
}
where i
is the increasing level of zoom
up to maxZoom
, res is the maximum extend of the the
bounds of the maps, div
is the tilSize
scaled by the initialResolution
. Ususally tileSize and initialResoulution should be the same than div=tileSize
outherweise div is calculated as div<-tileSize/initialRes*tileSize
.
If you are not able to find a good range of resolution but you find an example then you may just paste it in initialResolution
.
initialResolution=c(8192,4096,2048, 1024, 512, 256, 128,64, 32, 16, 8, 4, 2,1,0.5,0.25,0.125,0.0625)
This will be taken as it is.
If you don’t need or have no idea about the bounds
you may try to set at least one of the values to NULL
minx=NULL
with this option no bounds will be generated.
The ‘origin’ has always to be set. It this the upper left corner of the tiles and used as reference point for transforming the projected coordinates. Note you must use values of the target reference system. Especially in polar stereographic projections that are not centered on the pole this can be tricky.
Time for some examples…
First lets map the manually written input list from the tutorial. For convienience reason we load it from the package. We are obtaining the data from NASA EarthData Wiki. The used camps are documented in the package.
library(mapview)
library(raster)
# load data of the arctic stations
data("campsQ2")
# load the list of the above example
data("map.types")
mapview::projView(campsQ2, map.types = map.types$NASA)
In this special case of the antarctic NASA EarthData you may use the visEarthPole() function from the mapview package
# load data of the arctic stations
data("campsQ2")
# load the list of the above example
data("map.types")
nasa<-map.types$NASA
### use the visEarthPole function as a plugin
projView(campsQ2, map.types = nasa,
internalList = TRUE,
externalList = c("arctic-nasa","visEarthPole(groupList='1000',dateString='2014-02-04',createList = TRUE)"))
To avoid some strange side effects you’ll find further examples on seperates pages:
There are still more limitations than solutions. Actually the process of generating the correct map.types lists is more ore less cumbersome. On the other hand in the end there is always some manual work due to non strict service declarations. If somebody likes the function it could be worthwhile to automated this process.
In future releases I would like to
If you have any feedback, please don’t hesitate to contact me.
Bug reports should be filed at https://github.com/environmentalinformatics-marburg/mapview/issues
Cheers
Chris