How to update a pyshiny text input with title from clicked marker in ipyleaflet map - ipyleaflet

I'd like to update a text input in a pyshiny app each time a marker is clicked in an ipyleaflet map widget using the marker's title value. I have been using the marker.on_click() callback to get the title (station_id in this case) from the clicked marker, but I am struggling to dynamically change the text input box with the title text.
A basic working example that I have tried is shown below. The title of each marker is printed to screen from within the callback() function each time it is clicked. However, I can't figure out how to update the ui.input_text's value. The Reactive.Effect function does not seem to be called at all and I'm not sure why. What do I need to change here to update the value in the text input?
from shiny import App, reactive, run_app, ui
from shinywidgets import output_widget, register_widget
import ipyleaflet as ipyl
app_ui = ui.page_fluid(
{"class": "p-4"},
ui.row(
ui.column(
4,
ui.input_text("station_id", "Station ID:", value="Click on a station marker"),
),
ui.column(
8,
output_widget("map_widget")
),
),
)
def get_station_id(_marker):
def callback(*args, **kwargs):
print(_marker.title)
return _marker.title
return callback
def add_stations_to_map(_map, stations):
markers = []
for station in stations:
marker = ipyl.Marker(location=station[0], draggable=False, title=station[1])
_map.add(marker)
markers.append(marker)
return markers
def server(input, output, session):
# Initialize and display when the session starts (1)
_map = ipyl.Map(center=(-33.8, 151), zoom=8, scroll_wheel_zoom=True)
# Add a distance scale
_map.add(ipyl.leaflet.ScaleControl(position="bottomleft"))
register_widget("map_widget", _map)
# add markers and station id clik function
stations = [
[(-33.8, 151), "station_1"],
[(-33.8, 150), "station_2"],
]
markers = add_stations_to_map(_map, stations)
clicked_marker = "Click on a station marker"
for marker in markers:
clicked_marker = marker.on_click(get_station_id(marker))
#reactive.Effect
def _():
ui.update_text("station_id", value=clicked_marker)
app = App(app_ui, server, debug=True)
run_app(app, launch_browser=True, port=0)

You can use a reactive.Value to set your text:
clicked_marker_value = reactive.Value("Click on a station marker")
def get_station_id(_marker):
def callback(*args, **kwargs):
print(_marker.title)
clicked_marker_value.set(_marker.title)
return _marker.title
return callback
#reactive.Effect
def _():
ui.update_text("station_id", value=clicked_marker_value())

Related

Need to set border around PDAnnotationTextMarkup, Highlight text in PDF

Desired Result:
How do I set border(red) to my PDAnnotationTextMarkup? Just need to work with AdobePDF mainly.
def addCoordinates(cord: List[Coordinates], page) : PDPage = {
val annotations = page.getAnnotations
val borderThick = new PDBorderStyleDictionary()
borderThick.setWidth(0.8.toFloat)
borderThick.setStyle(PDBorderStyleDictionary.STYLE_SOLID)
cord.foreach {
val textM = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT)
textM.setBorderStyle(borderThick)
textM.setColor(yellow)
// Set rectange and quads
annotations.add(markup) // Need to set border to markup?
// Below code i was able to get red border with Square annotation.
// This is more kind of workaround, see two annotations in Adobe PDF (not user friendly.)
val aSquare = new PDAnnotationSquareCircle(PDAnnotationSquareCircle.SUB_TYPE_SQUARE)
aSquare.setColor(red);
// set rectange and quads
annotations.add(aSquare)
}
page
}

Leaflet change layer color when click on marker

I am trying to change the fill color of the State in the usa map when the marker on that state is clicked. I have been able to get the state color to change when clicking on the state but cannot get the color to change when clicking on the marker.
The map is here: https://www.thekeithcorp.com/map-properties/
I am trying to use a click event listener, the flyto is working as its supposed to but not the color change.
This is my code:
shelter1.addEventListener("click", function (e){
map.flyTo([35.7,-79.0], 7);
restyleLayer(name, layer);
});
function getColor(name) {
color: 'red'
}
function restyleLayer(name, layer) {
layer.feature.properties.name = 'North Carolina';
var myFillColor = getColor(name);
layer.setStyle(myFillColor);
}
Any help would be appreciated!!

Leaflet several featuregroups order changes when toggled

I am using Leaflet.js to create a map. The data displayed on the map depends on user selection so I instantiate two empty feature groups on map load, one for the values, and one for a color marker behind the value ( color depends on the value ).
if( dHMT.dataColorLayer===undefined ){
dHMT.dataColorLayer = new L.featureGroup({}).addTo(dHMT.map);
}
if( dHMT.dataValueLayer===undefined ){
dHMT.dataValueLayer = new L.featureGroup({}).addTo(dHMT.map);
}
I then add the empty layers to the layer switcher.
dHMT.overlayMapsLS = {
"Bassins ": dHMT.bassinLayer,
"Couleurs ": dHMT.dataColorLayer,
"Données ": dHMT.dataValueLayer
};
Once the user selects data, the featureGroups are filled with the relevant values/markers.
var iconColor = L.divIcon({className: 'dataSpans',html:"<div style='text-align: center;border-radius: 50%;height:40px;width:40px;padding-top:9px;background:"+dHMT.siteinfo[x].color+"'></div>"});
var iconColorDiv = L.marker([dHMT.ecartArray[x].lat, dHMT.ecartArray[x].lon], {icon: iconColor})
.bindPopup(
'Nom : '+dHMT.siteinfo[x].name+'<br>'+
'Numéro : '+dHMT.siteinfo[x].stnm+'<br>'+
'Stid : '+dHMT.siteinfo[x].stid+'<br>'+
'LatLon : '+dHMT.siteinfo[x].lat+','+dHMT.ecartArray[x].lon+'<br>'+
'Valeur : '+dHMT.ecartArray[x].ecart+'<br>'
).on('mouseover', function (e) {
this.openPopup();
}).on('mouseout', function (e) {
this.closePopup();
});
var iconValue = L.divIcon({className: 'dataSpans',html:"<div style='text-align: center;height:40px;width:40px;padding-top:9px;'>"+value+"</div>"});
var iconValueDiv = L.marker([dHMT.ecartArray[x].lat, dHMT.ecartArray[x].lon], {icon: iconValue});
dataColorFeatures.push(iconColorDiv);
dataValueFeatures.push(iconValueDiv);
L.featureGroup(dataColorFeatures).addTo(dHMT.dataColorLayer);
L.featureGroup(dataValueFeatures).addTo(dHMT.dataValueLayer);
Both layers are fine, I have a nice map with a marker layer with colored circles with another layer displaying values over the marker. The goal being to deactivate the colors or the values using the layer switcher.
The problem is that if, for example, I toggle the color layer off, and turn it on again, the colored circles reappear over the values. The desired behavior would be to have them reappear in the original order, behind the values.
You can listen to the overlayadd event on your L.Map instance:
Fired when an overlay is selected through the layer control.
http://leafletjs.com/reference.html#map-overlayadd
When fired you can use the bringToFront method of L.FeatureLayer:
Brings the layer group to the top of all other layers.
http://leafletjs.com/reference.html#featuregroup-bringtofront
map.on('overlayadd', function () {
dHMT.dataValueLayer.bringToFront();
});

Can't set text on pushpins in Bing Maps

I'm following the example here but I don't get to see the text, only the pushpins themselves. What can I be missing?
for (index in centers)
map.entities.push(new Microsoft.Maps.Pushpin(centers[index]),
{ text: "A", visible: true, textOffset: new Microsoft.Maps.Point(0, 5) });
The array centers is just a set of points and, since I get the pushpins to appear at the right locations, I believe that the error is in the options.
Note that I'm trying to reuse the default pushpins as far as I can and intentionally omit the custom icon. In case I have to use a custom icon (which would be a shame), where can I find a set of pushpins looking like the default one (possibly in various colors)?
From bing interactive sdk.
Just delete the line with the custom pin from you pushpinOptions and you will get the default push pin with text.
The error in you code that you pass the pushpinOptions as parameter to map.entities.push method, but the options is a parameter of new Microsoft.Maps.Pushpin method.
var offset = new Microsoft.Maps.Point(0, 5);
var pushpinOptions = { text : '1', visible: true, textOffset: offset};
var pushpin= new Microsoft.Maps.Pushpin(
new Microsoft.Maps.Location(47.6, -122.33),
pushpinOptions);
map.entities.push(pushpin);

Coffeescript dynamically create/call a function from a list on select box value change

I'm working on adding some image editing tools using the Pixastic library. The idea is that the user can choose an aspect of the image or tool they want from a select box, then the tool will show up below the select box (I'm using select2) and the user can edit via a slider. Here's what I have so far:
# This seeds the select2 options list
imageToolsList = [
{id: 'bl', text: 'Blur'}
{id: 'bc', text: 'Brightness/Contrast'}
{id: 'ca', text: 'Color Adjust (RGB)'}
...
]
#Creates a select box and calls imageTooler function when the value of the box is changed
$(".image_tools_select").each ->
$(#).select2
placeholder: "Select an adjustment tool."
data: imageToolsList
$(#).on("change", (i) ->
imageTooler JSON.stringify(
val: i.val
clipName: $(#).closest('.clip').attr('id')
)
)
# The function called on the value that the select box is changed to
imageTooler = (i) ->
imageData = jQuery.parseJSON(i)
iId = imageData.val
imageClipName = imageData.clipName
newTool = "<div id=#{iId}><label>#{iId}</label><div class='slider#{iId}'></div></div>"
$("##{imageClipName}").find(".imagetoolfields").append newTool
This succeeds in appending the name of the editing tool and the correct slider div beneath the select box when a tool is chosen, but what I'd really like is dynamically create a slider function for that particular tool and image (there are multiple images on a page, each with their own editing toolbelt). Here's a slider function that works for a the 'Blur' tool:
$('.sliderbl').slider
min: 0
max: 5
value: 0.5
step: 0.1
range: "min"
slide: (event, ui) ->
$("#img_snapshot_16").pixastic("blurfast", {amount:ui.value})
Is there a way to expand the imageToolsList so that it looks something like:
imageToolsList = [
{id: 'bl', text: 'Blur', tool: $("##{imageClipName}").pixastic("blurfast", {amount:ui.value}), sliderVals: {min: 0, max: 5, value: 0.5, step: 0.1, range: "min"} }
...
]
and then dynamically create the jQuery slider functions for each tool in imageTooler, as is being done with the div and slider div?
Comments get a little tedious for anything complicated so I'll just go ahead and map it all out. I've made a few assumptions about what is defined where and when but I don't think the assumptions matter that much.
We'll start with a simplified case: just one object similar to what you have in imageToolsList:
{
id: 'bl'
text: 'Blur'
sliderVals: { min: 0, max: 5, value: 0.5, step: 0.1, range: "min" }
tool: (imageClipName) ->
(event, ui) -> $("##{imageClipName}").pixastic("blurfast", {amount:ui.value})
}
I've tweaked the order a little bit and switched tool to a function which returns a function. We don't want the pixastic call to happen while you're defining the object literals in imageToolsList, making tool a function allows us to defer the pixastic execution until later. Since we (presumably) don't know what imageClipName should be when we define imageToolsList, we need another function to allow us to fill that in with, again, calling pixastic until even later; hence the function returning a function trick.
Given one of these, how do we build a slider call? All we need to do is copy sliderVals (to avoid changing imageToolsList) and fill in the slide function:
sliderDef = { id: 'bl', ... }
doTheSliderThing = (imageClipName) ->
slide = sliderDef.tool(imageClipName)
args = $.extend({ }, sliderDef.sliderVals, slide: slide)
$(".slider#{sliderDef.id}").slider(args)
# And make it all go and pixastic-ify `#pancakes`.
doTheSliderThing('pancakes')
tool is a function which returns a callback function so sliderDef.tool(imageClipName) gives us the appropriate
(event, ui) -> $(...).pixastic(...)
callback function.
If we have an id and we want the appropriate entry from imageToolList, then we have to go looking for it:
# If the list is short:
[sliderDef] = (o for o in imageToolList when o.id == id)
The for loop gives you an array back and then the [sliderDef] unwraps that array and leaves the single result in sliderDef. If the imageToolList is longer then you'd want to short-circuit the loop and bail out as soon as you have a result:
# Longer list, bail out as soon as we've found what we're looking for.
for o in imageToolList when o.id == 2
sliderDef = o
break
or better, rework the structure of imageToolList to allow direct access by id:
# Even longer list: allow direct access by `id`.
imageToolList =
bl: { text: 'Blur', sliderVals: { ... }, ... }
...
and then we can do things like this:
doTheSliderThing = (id, imageClipName) ->
sliderDef = imageToolList[id]
slide = sliderDef.tool(imageClipName)
args = $.extend({ }, sliderDef.sliderVals, slide: slide)
$(".slider#{id}").slider(args)
# And make it all go and pixastic-ify `#pancakes` using `'bl'`.
doTheSliderThing('bl', 'pancakes')
Or, if you prefer to be terse:
doTheSliderThing = (id, imageClipName) ->
$(".slider#{id}").slider($.extend({ }
imageToolList[id].sliderVals
slide: imageToolList[id].tool(imageClipName)
))
Update for the comments: If you have this:
sliderDefs =
bl: { text: 'Blur', sliderVals: { ... }, ... }
...
Then you can build the stuff that slider2 wants like this:
opts = ({id: k, text: v.text} for k,v of sliderDefs)