R leaflet easybutton : reset view to layer bbox - r

I'm having some troubles with the easybutton leaflet plugin in my Shiny App.
What I'm trying to do is to recenter the view on my points layer when the easybutton is clicked :
...
addCircleMarkers(lng = points$long,
lat = points$lat,
weight = 1, radius = 4,
group = "points",
...
addEasyButton(easyButton(
icon = 'ion-arrow-shrink',
title = 'Reset view',
onClick = JS("function(btn, map) {map.fitBounds(points.getBounds()); }")
))
But it doesn't work : "points is not defined" is printed in the JS console.
How can I get the real leaflet name (JS) of my points layer ?
Thank you.

Despite being a group name, points is not defined within the javascript - you need to use the layerManager to find layers - and passing the layer name from R to js is not quite as straightforward as one might hope.
This is not very clear in the documentation, but you should be able to set a group name for the markers, as you have, and then access it like so:
onClick = JS("function(btn, map) {
var groupLayer = map.layerManager.getLayerGroup('groupName');
}")
To get the bounds you should be able to use:
onClick = JS("function(btn, map) {
var groupLayer = map.layerManager.getLayerGroup('groupName');
map.fitBounds(groupLayer.getBounds());
}")

Related

Adding just an arbitrary image to a leaflet map

I am trying to use leaflet to show a smaller map than usual so I don't want to use the normal tiling system. I don't care about smooth zooming and loading higher resolution tiles when needed. Instead I am trying to add a raster image from an image file. Lets say this file that comes up when I google "hand drawn map"
So I try
download.file('https://external-preview.redd.it/7tYT__KHEh8FBKO6bsqPgC02OgLCHAFVPyjdVZI4bms.jpg?auto=webp&s=ff2fa2e448bb92c4ed6c049133f80370f306acb3',
destfile = 'map.jpg')
map = raster::raster('map.jpg')
# it seems like i need a projection to use a raster image.
# not sure what controls do I have over this, especially in
# absence of a proper map layer and it's likely
# part of the solution
crs(map) = CRS("+init=epsg:4326")
leaflet() %>%
leaflet::addRasterImage(map)
The resulting output is nothing like the input image
How do I take an arbitrary image and place in on a leaflet map?
I failed to find the exact reason why addRasterImage fails here but I found reports that it doesn't behave well on L.CRS.Simple projection, which is what you'll want to use to show a simple rectangle image.
Using htmlwidgets::onRender makes it possible to directly use the javascript function L.imageOverlay to add the image you want
library(leaflet)
# minimal custom image
imageURL = 'https://external-preview.redd.it/7tYT__KHEh8FBKO6bsqPgC02OgLCHAFVPyjdVZI4bms.jpg?auto=webp&s=ff2fa2e448bb92c4ed6c049133f80370f306acb3'
# get image data. we'll use this to set the image size
imageData =
magick::image_read(imageURL) %>% image_info()
leaflet(options = leafletOptions(crs = leafletCRS('L.CRS.Simple'))) %>%
htmlwidgets::onRender(glue::glue("
function(el, x) {
var myMap = this;
var imageUrl = '<imageURL>';
var imageBounds = [[<-imageData$height/2>,<-imageData$width/2>], [<imageData$height/2>,<imageData$width/2>]];
L.imageOverlay(imageUrl, imageBounds).addTo(myMap);
}
",.open = '<', .close = '>'))
For a large image like this if you want to make the image smaller you can either scale down using the imageBounds in javascript side or set minZoom to a negative value and use setView to start out zoomed out.
leaflet(options =
leafletOptions(crs = leafletCRS('L.CRS.Simple'),
minZoom = -1)) %>%
setView(0,0,zoom = -1) %>%
htmlwidgets::onRender(glue::glue("
function(el, x) {
var myMap = this;
var imageUrl = '<imageURL>';
var imageBounds = [[<-imageData$height/2>,<-imageData$width/2>], [<imageData$height/2>,<imageData$width/2>]];
L.imageOverlay(imageUrl, imageBounds).addTo(myMap);
}
",.open = '<', .close = '>'))

Displaying image on point hover in Plotly

Plotly allows you to display text fields when hovering over a point on a scatterplot. Is it possible to instead display an image associated with each point when the user hovers over or clicks on it? I am mostly just using the web interface, but I could instead push my ggplot from R.
Unfortunately, there is no easy way to display images on hover on plotly graphs at the moment.
If you are willing to learn some javascript, plotly's embed API allows you to customize hover (as well as click) interactivity.
Here is an example of a custom hover interaction showing images on top of a plotly graph. The javascript source code can be found here.
Images on hover is now available by Plotly lib. Here is a sample:
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
# Small molcule drugbank dataset
# Source: https://raw.githubusercontent.com/plotly/dash-sample-apps/main/apps/dash-drug-discovery/data/small_molecule_drugbank.csv'
data_path = 'datasets/small_molecule_drugbank.csv'
df = pd.read_csv(data_path, header=0, index_col=0)
fig = go.Figure(data=[
go.Scatter(
x=df["LOGP"],
y=df["PKA"],
mode="markers",
marker=dict(
colorscale='viridis',
color=df["MW"],
size=df["MW"],
colorbar={"title": "Molecular<br>Weight"},
line={"color": "#444"},
reversescale=True,
sizeref=45,
sizemode="diameter",
opacity=0.8,
)
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
xaxis=dict(title='Log P'),
yaxis=dict(title='pkA'),
plot_bgcolor='rgba(255,255,255,0.1)'
)
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['IMG_URL']
name = df_row['NAME']
form = df_row['FORM']
desc = df_row['DESC']
if len(desc) > 300:
desc = desc[:100] + '...'
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
html.H2(f"{name}", style={"color": "darkblue"}),
html.P(f"{form}"),
html.P(f"{desc}"),
], style={'width': '200px', 'white-space': 'normal'})
]
return True, bbox, children
if __name__ == "__main__":
app.run_server(debug=True)
More info: https://dash.plotly.com/dash-core-components/tooltip

Openlayers marker size

I have an openlayers map with markers added as geometry vector points. In the style option I set a size for each. However, the problem is, that if I zoom in or zoom out, they all become the same size until I load the entire page again. In other words, once I zoom in or out, they are all the same.
var layer_style = OpenLayers.Util.extend({},
OpenLayers.Feature.Vector.style['default']);
var style = OpenLayers.Util.extend({}, layer_style);
var pointLayer = new OpenLayers.Layer.Vector("Point Layer");
map.addLayers([terrain, road, satellite, hybrid, pointLayer]);
var lonlat = new OpenLayers.LonLat(0, 140);
lonlat.transform(proj, map.getProjectionObject());
map.setCenter(lonlat, 2);
var point = new OpenLayers.Geometry.Point(-40, -40);
point = point.transform(proj, map.getProjectionObject());
style.pointRadius = 10;
var pointFeature = new OpenLayers.Feature.Vector(point, null, style);
pointLayer.addFeatures([pointFeature]);
var point = new OpenLayers.Geometry.Point(-40, -40);
point = point.transform(proj, map.getProjectionObject());
style.pointRadius = 40;
var pointFeature = new OpenLayers.Feature.Vector(point, null, style);
pointLayer.addFeatures([pointFeature]);
When I load this, I get two markers, one size 10, the other 40. But when I zoom in or out, they all become same size.
You are overwriting the pointRadius property of the style object each time, so in the end the last value will be used as OpenLayers will only point to the style.
What you need to do is use a lookup to let the pointRadius depend on a given feature attribute.
See Rule-based Styling: http://trac.osgeo.org/openlayers/wiki/Styles#Rule-basedStyling

Zoom into group of points in Flex

I have an application in Flex 4 with a map, a database of points and a search tool.
When the user types something and does the search it returns name, details and coordinates of the objects in my database.
I have a function that, when i click one of the results of my search, it zooms the selected point of the map.
The question is, i want a function that zooms all the result points at once. For example if i search "tall trees" and it returns 10 points, i want that the map zooms to a position where i can see the 10 points at once.
Below is the code im using to zoom one point at a time, i thought flex would have some kind of function "zoom to group of points", but i cant find anything like this.
private function ResultDG_Click(event:ListEvent):void
{
if (event.rowIndex < 0) return;
var obj:Object = ResultDG.selectedItem;
if (lastIdentifyResultGraphic != null)
{
graphicsLayer.remove(lastIdentifyResultGraphic);
}
if (obj != null)
{
lastIdentifyResultGraphic = obj.graphic as Graphic;
switch (lastIdentifyResultGraphic.geometry.type)
{
case Geometry.MAPPOINT:
lastIdentifyResultGraphic.symbol = objPointSymbol
_map.extent = new Extent((lastIdentifyResultGraphic.geometry as MapPoint).x-0.05,(lastIdentifyResultGraphic.geometry as MapPoint).y-0.05,(lastIdentifyResultGraphic.geometry as MapPoint).x+0.05,(lastIdentifyResultGraphic.geometry as MapPoint).y+0.05,new SpatialReference(29101)).expand(0.001);
break;
case Geometry.POLYLINE:
lastIdentifyResultGraphic.symbol = objPolyLineSymbol;
_map.extent = lastIdentifyResultGraphic.geometry.extent.expand(0.001);
break;
case Geometry.POLYGON:
lastIdentifyResultGraphic.symbol = objPolygonSymbol;
_map.extent = lastIdentifyResultGraphic.geometry.extent.expand(0.001);
break;
}
graphicsLayer.add(lastIdentifyResultGraphic);
}
}
See the GraphicUtil class from com.esri.ags.Utils package. You can use the method "getGraphicsExtent" to generate an extent from an array of Graphics. You then use the extent to set the zoom factor of your map :
var graphics:ArrayCollection = graphicsLayer.graphicProvider as ArrayCollection;
var graphicsArr:Array = graphics.toArray();
// Create an extent from the currently selected graphics
var uExtent:Extent;
uExtent = GraphicUtil.getGraphicsExtent(graphicsArr);
// Zoom to extent created
if (uExtent)
{
map.extent = uExtent;
}
In this case, it would zoom to the full content of your graphics layer. You can always create an array containing only the features you want to zoom to. If you find that the zoom is too close to your data, you can also use map.zoomOut() after setting the extent.
Note: Be careful if you'Ve got TextSymbols in your graphics, it will break the GraphicUtil. In this case you need to filter out the Graphics with TextSymbols
Derp : Did not see the thread was 5 months old... Hope my answer helps other people

How can I update the label of a Polygon Vector in OpenLayers after it has been drawn?

I am quite new to OpenLayers. Right now, I have a polygon vector with some styling applied and a label.
var style = $.extend(true, {}, OpenLayers.Feature.Vector.style['default']);
style.pointRadius = 15;
style.label = "My Polygon";
style.fillColor = #f00;
style.strokeColor = #000;
var styleMap = new OpenLayers.StyleMap({"default" : style});
var polygonLayer = new OpenLayers.Layer.Vector("Polygon Layer", {styleMap: styleMap});
At some point after doing some processing, I want to display the result as a label. How can I update the label? I figure it would be something like this, but this wasn't the way.
polygonLayer.options.styleMap.styles.label = "Updated label";
Thanks in advance.
You are on the right way. You can set new label for all features in a layer like that:
polygonLayer.styleMap.styles.default.defaultStyle.label = "new label";
polygonLayer.redraw();
As you see it's important to call redraw() method after you set new value.
That's how you change label for all features in a layer. Quite often though you'll need to set new labels per feature. To achieve that you should do following when you create pollygonLayer:
var style = $.extend(true, {}, OpenLayers.Feature.Vector.style['default']);
style.label = "${feature_name}";
Each feature has a collection of attributes. In this case value of attribute feature_name will be displayed as a label. To change label value per feature you simply change value of the attribute on that feature and then of course call redraw() on layer.

Resources