Leaflet VideoOverlay controls - leaflet

In one of my projects, I have to integrate video on a leaflet Map. I didn't use the extend layer method because the video can have arbitrary dimension and should behave as other elements's map. I'm using a videoOverlay element to do so.
But now I have a problem, how to provide a proper way for the users to controls the video? I try to add the attribute controls inside the html video tag but the controls don't appears.
So I think that my only solution now is to make an external Leaflet controls right ?
Is it possible to have the controls on the video, as classical html video tag ?

First, the getElement() method of L.VideoOverlay to get the relevant instance of HTMLVideoElement.
Then, since HTMLVideoElement is a subclass of HTMLMediaElement, it has a controls property you can modify.
Put together it should look like:
var overlay = L.videoOverlay( /* stuff */ ).addTo(map);
var el = overlay.getElement();
el.controls = true;
or even:
var overlay = L.videoOverlay( /* stuff */ ).addTo(map);
overlay.getElement().controls = true;
Please note that the HTMLVideoElement instance only exists after the L.VideoOverlay has been added to a L.Map.

Related

gtkmm4: hide Gtk::Video's Gtk::MediaControls

I'm not exactly a fan of how Gtk::Video's controls look and I want to expand on them with my own custom controls, but I'm not sure how to hide the default media controls. Is there any way that I can do this?
I know how to do it in GTK. You will have to only find the corresponding methods in GTKMM. GtkVideo widget contains a GtkOverlay. In that overlay are three elements GtkPicture, GtkImage, GtkRevealer. GtkPicture is the video surface. GtkImage is that big play button in the center of video. And GtkRevealer is the block of controls. So basically I am hiding or removing the last child of the first child of GtkVideo.
GtkMediaFile* videoFile = GTK_MEDIA_FILE(gtk_media_file_new_for_file(g_file_new_for_path("GTK4Video.mp4")));
GtkVideo* videoPlayer = GTK_VIDEO(gtk_video_new_for_media_stream(GTK_MEDIA_STREAM(videoFile)));
GtkOverlay* videoOverlay = GTK_OVERLAY(gtk_widget_get_first_child(GTK_WIDGET(videoPlayer)));
GtkRevealer* videoControls = GTK_REVEALER(gtk_widget_get_last_child(GTK_WIDGET(videoOverlay)));
gtk_widget_hide(GTK_WIDGET(videoControls));
//gtk_overlay_remove_overlay(videoOverlay, GTK_WIDGET(videoControls));
From the documentation of Gtk.Video:
The controls are available separately as Gtk.MediaControls. If you
just want to display a video without controls, you can treat it like
any other paintable and for example put it into a Gtk.Picture.

Disabling edit of Mapbox GL Draw polygons

When creating a polygon using Mapbox GL Draw I do not want the polygon to then be editable, clickable etc. After ending the draw event, I'd like to to just appear as finished like below when it's deselected.
Maybe this is embedded in the simple_select config option?
The Mapbox Draw plugin provides an interface for writing and hooking in custom modes, where a mode is defined as way to group sets of user interactions into one behavior. Mapbox Draw Static Mode is a custom mode that displays data stored in Draw but does not let users interact with it, which sounds like what you are looking for.
modes.simple_select.onDrag function disable the onDrag event.
I give you a small example to can contextualize it.
Inside your #map-init method you should have:
const modes = MapboxDraw.modes;
let draw = new MapboxDraw({
// your mapBoxDraw options
});
map.on("draw.create", updateArea);
modes.draw_polygon.clickAnywhere = function (state, e) {
//your polygon click restrictions
}
modes.simple_select.onDrag = function (state, e) {
//when polygon is deselected onDrag will be false and user not be able to drag it
};
function updateArea(e) {
// your stuff when you're drawing
}

No setBounds function for Leaflet imageOverlay

I'm reading an imageOverlay URL from an ArcGIS webserver that uses the leaflet getBound() coordinates as part of the URL (we have large maps that are filtered for the current window 'extent'). Apologies for not including the actual path (I'm working with sensitive client data). Eg:
http://myarcgiswebserver.com/MapServer/export/dpi=96&format=png32&bbox=27.119750976562504%2C-31.194007509998823%2C32.39044189453126%2C-29.692824739380754&size=1719%2C434
[bbox] = current imageBounds
When dragging my map the imageOverlay url is updated correctly but my leaflet window is no longer aligned to the imageBound values that were set when first adding the imageOverlay which results in a skewed output (this is my assumption):
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Am i approaching this problem incorrectly or would the introduction of a function to update the current imageBounds resolve this? Perhaps not a new function but the expansion of setUrl with additional parameters...?
Many thanks for any feedback...
As #ghybs pointed out, your use case might be better served by using the WMS
interface of your ArcGIS server.
Anyway, you say
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Well, that glitch is due to you probably doing something like:
Remove old overlay
Add new overlay
Wait until the image is received from the network
Wait one frame so the new overlay is shown
and instead you should be doing something like:
Add new overlay
Wait until the image is received from the network
Remove old overlay
Wait one frame so the new overlay is shown
The problem is just the async wait and the possible race conditions there, but should be easy to hack together, e.g.:
var activeOverlay = null;
var overlayInRequest = null;
map.on('moveend zoomend', {
// If we are already requesting a new overlay, ignore it.
// This might need some additional debouncing logic to prevent
// lots of concurrent requests
if (overlayInRequest) {
overlayInRequest.off('load', showOverlay);
}
overlayInRequest = L.imageOverlay( computeUrl( map.getBounds() ), myOverlayOptions );
overlayInRequest.on('load', showOverlay);
});
function showOverlay(ev) {
activeOverlay.remove();
activeOverlay = overlayInRequest;
activeOverlay.addTo(map);
overlayInRequest = undefined;
}
If you use an ImageOverlay but change its url dynamically, with a new image that reflects a new bounding box, then indeed that is the reason for the behaviour you describe: you display an image that has been generated using a new bbox, but positioned in the initial bbox, since the image overlay remains at the same geographical position on the map.
Instead, it sounds to me that you should use a TileLayer.WMS.
It would automatically manage the bounding box update for you. You may need to find the correct options to fit your service provider required URL syntax, though.
Example: http://playground-leaflet.rhcloud.com/yel/1/edit?html,output

Chrome Extension: Access to frame elements

I am developing a chrome extension (content script) to access web page content. I use the DOM to access all elements in page.
My code does not work correctly when the web page contains "frameset". In this matter I can count the frame number but I can't access to the frame content.
I use this code to count frame objects available on current page :
for frameset :
parent.frames.length
and for iframe :
document.getElementsByTagName("iframe").length
but the following code does not work :
parent.frames[0]
and returns "undefined".
My question:
How can I access to frame set elements inside a chrome extension (for both iframe and frameset)?
Similar to how Jerinaw approached it, I did the following via jQuery:
// Find the frame
var frame = $(document).find("frame[name='nameOfFrame']")[0];
// Callback once frame's internals are loaded
$(frame).load(function() {
accessFrame();
});
function accessFrame() {
// The following line is really important to accessing the frame's content.
var frameContent = frame.contentDocument;
// Notice that $(".objectSelectorClass") won't discover the object you're hunting for. You have to find it within the frame's contentDocument.
var objectInFrame = $(frameContent).find(".objectSelectorClass");
var objectInFrame_AlternateSyntax = $(".objectSelectorClass", frameContent)
// do stuff to objectInFrame
}
I'm running into the same problem. The frames are not iframes and they are in a frameset. What I had to do was an onload (for some reason addEventListener isn't working) to the frame but through it's contentWindow
for example:
From the main document
document.querySelector('#SomeFrameSelector').contentWindow.document.body.onload
Accessing the frame this way got me to the frames document and allowed me to attach the onload. Once fired using the same selector I was able to modify the frames content.

How do I center and show an infobox in bing maps?

My code does a .pantolatlong then a .showinfobox
The info box does not appear, unless I remove the pantolatlong. I guess it is stopping it. I tried adding it to the endpan event but that did not work.
What is the simplest way to pan to a pushpin and display the infobox for it?
I was using setcenter, but I discovered that sometimes setcenter pans, and this breaks it.
After some insane googling, I came up with the solution, and I'll share it here so that others can hopefully not have the grief I went through.
I created and power my bing map using pure javascript, no sdk or iframe solutions. In my code, I generate the javascript to add all of the pins I want to the map, and inject it using an asp.net label.
If you call the setCenter() method on your Bing Map, it is supposed to instantly set the map, surprise surprise, to the coordinates you specify. And it does... most of the time. Occasionally though, it decides to pan between points. If you do a SetCenter, followed by a ShowInfoBox, it will work great, unless it decides to pan.
The solution? Being great programmers we are, we dive into the sdk, and it reveals there are events we can hook into to deal with these. There is an onendpan event, which is triggered after a pan is completed. There is also an onchangeview event, which triggers when the map jumps.
So we hook into these events, and try to display the infobox for our pushpin shape... but nothing happens. Why not?
You have to give it a few milliseconds to catch its breath, for unknown reasons, when the event is called. Using a setTimeout with 10 milliseconds seems to be fine. Your box will appear great after this.
The next problem is, you only want it to appear when it pans via whatever you used to make it flick between your pushpins (in my case, a table with onclick methods). I create/destroy the event handlers on the fly, although there are other options such as using a global variable to track if the user is panning, or if the system is panning in response to a click.
Finally, you have the one bug that comes from this. If you click a place in your list, and it jumps/pans to that location, the infobox will display fine. If the user dismisses it though, then clicks again on the list item, the map does not move, and therefore no events are triggered.
My solution to this is to detect if the map moved or not, by recording its long/lat, and using another setTimeout method, detecting if they changed 100ms later. If they did not, display the infobox.
There are other things you need to keep track of, as there is no way I can see to pass parameters to the eventhandlers so I use global javascript variables for this - you have to know which pushpin shape you are displaying, and also keep track of the previous mapcoordinates before checking to see if they changed.
It took me a while to piece all this together, but it seems to work. Here is my code, some sections are removed:
// An array of our pins to allow panning to them
var myPushPins = [];
// Used by the eventhandler
var eventPinIndex;
var oldMapCenter;
// Zoom in and center on a pin, then show its information box
function ShowPushPin(pinIndex) {
eventPinIndex = pinIndex;
oldMapCenter = map.GetCenter();
map.AttachEvent("onendpan", EndPanHandler);
map.AttachEvent("onchangeview", ChangeViewHandler);
setTimeout("DetectNoMapChange();", 200);
map.SetZoomLevel(9);
map.SetCenter(myPushPins[pinIndex].GetPoints()[0]);
}
function EndPanHandler(e) {
map.DetachEvent("onendpan", EndPanHandler);
setTimeout("map.ShowInfoBox(myPushPins[eventPinIndex]);", 10);
}
function ChangeViewHandler(e) {
map.DetachEvent("onchangeview", ChangeViewHandler);
setTimeout("map.ShowInfoBox(myPushPins[eventPinIndex]);", 10);
}
function DetectNoMapChange(centerofmap) {
if (map.GetCenter().Latitude == oldMapCenter.Latitude && map.GetCenter().Longitude == oldMapCenter.Longitude) {
map.ShowInfoBox(myPushPins[eventPinIndex]);
}
}
Here is another way:
function addPushpin(lat,lon,pinNumber) {
var pinLocation = new Microsoft.Maps.Location(lat, lon);
var pin = new Microsoft.Maps.Pushpin(map.getCenter(), { text: pinNumber.toString() });
pinInfobox = new Microsoft.Maps.Infobox(pinLocation,
{ title: 'Details',
description: 'Latitude: ' + lat.toString() + ' Longitude: ' + lon.toString(),
offset: new Microsoft.Maps.Point(0, 15)
});
map.entities.push(pinInfobox);
map.entities.push(pin);
pin.setLocation(pinLocation);
map.setView({ center: pinLocation});
}