mapBOX - find the element ID on click - javascript

Context:
I have my project setup that draws a road on the mapbox as layers (the road is basically a line with styling and painting implemented). Now I need to click an existing line, and wherever I click it, I want to draw a sub-road originating from the point it clicked it
Blue line is Actually line, the surrounding is just styling.
What I want:
I want to get the ID of the line element (or main road), and the point where it is clicked and then when user has clicked on target coordinate on the map, I want to grab that info and add it into existing line so that the line becomes a single with additional breakpoints. This will make the line look like
(the line is single yet it has multiple stops. This makes it consistent. Feels part of line)
The Problem:
I click on the road (the line), and then click somewhere else to stop drawing, I see new road drawn but since it is a separate layer, it looks not consistent with existing one.
Findings:
Every line, every shape is separate layer, since first road, even though it had multiple stops, was in-fact a single road so the design was consistent 3. But since the new line is a new layer, it has its own styling.
I need to find a way to add new data in existing layer
Roadblocks:
Need to find the ID of existing element when I click on it to use it as a source for my new line
Need the pin-point location of click
Need the data for new line/ new layer
find a way to merge into existing layer
Now with all the above context in mind, I am not even able to get the ID of element
SideNote:
While drawing a line in mapbox (using draw.changeMode('draw_line_string'); and then a create various stop points by clicking on map geoJsons) that line stays in one piece even though it has various sub-lines.
Now I need to grab the ID of that line and then specific point when I did click.
Here is my relavent parts of code
draw the line on map
draw.changeMode('draw_line_string');
map.on('draw.create', configureAndUpdateMapBoxDevices);
in configureAndUpdateMapBoxDevices function
draw.add(e.features[0]); //updating event real time for all callbacks where e is passed on as argument so its have these properties.
const json_data = {
json_id: e.features[0].id,
json_string: JSON.stringify(e.features[0])
};
updateMapboxDevice(selected_edit_site_id, json_data); // update on db and models
to style the road defined various variable layers for main road element
map.addLayer(bottomLine);
map.addLayer(left_yellowBelt);
map.addLayer(right_yellowBelt);
map.addLayer(topLayer);
map.addLayer(lineSeparator2L);

Related

Cesium -- on click, change map point to standard marker with icon

I am attempting to mimic the the functionality one would see using the Waze LiveMap (https://www.waze.com/livemap) in Cesium. When a point is clicked, it is converted to a marker with an icon.
I've attempted a few different things with varying levels of success and I'm at my wits end. Does anyone have a good suggestion as to how I can proceed?
There are a couple different ways of doing this, some high-level and some lower-level. The high-level one is easiest so I'll start there. The Cesium Viewer fires an event called selectedEntityChanged when its own selection changes, and you can wire that up to toggle billboard show flags on and off.
Here's a demo. For this demo, I took the original map pins demo and added some lines of code: I turned off the show flags for all the billboards at the start, I added points to stand in for the now-hidden billboards, and I added the following block of code to toggle the billboard show flags when selected, like this:
var lastSelectedPin;
viewer.selectedEntityChanged.addEventListener(function(newEntity) {
if (lastSelectedPin && lastSelectedPin.billboard) {
lastSelectedPin.billboard.show = false;
}
lastSelectedPin = newEntity;
if (lastSelectedPin && lastSelectedPin.billboard) {
lastSelectedPin.billboard.show = true;
}
});
This uses Cesium Viewer's own selection system, but toggles the billboard show flag to appear when selected and disappear when de-selected.
Alternatively, you can dig into Cesium's lower levels for finer control, but there will be a longer learning curve. The Picking Demo shows off several types of picking operations, including scene.pick, scene.drillPick, and camera.pickEllipsoid, which offer various ways to detect what contents exist at a particular screen location. Often these functions are called in response to mouse movements or clicks, or touch/pointer events, to see what the user is interacting with.

Update map panes and legend dynamically on leaflet

I'm trying to use leaflet to make an interactive map (like a choropleth) using data from geojson files and I've a problem to update data and legend on the map, dynamically.
My code use a leaflet map with a mapbox-light baselayer, a dropdownlist and a legend. Two jsons files are loaded and I use two map panes to use them and set their z-index. Dropdownlist has two options and is used to change the selected geojsons to expand this example; the real case has 25 values to choose.
I use $(id_dropdownlist).change to make everything. If ddl change:
remove panes if these exists.
select geojsons files using ddl value (default url myjson files + ajax for this example).
add panes, load and color polygons.
When I make the first change in the ddl works fine: select geojsons files, load and color polygons. My problem appears from the second change onwards, after remove panes: geojsons are loaded successfully but these are not showed/colored on my map.
My amateur debugging detect than in the first change the div leaflet-pane element has the geojsons values, but from the second change onwards the same div leaflet-pane has not data.
Also, I want to update the legend at same time after load the geojsons, but I do not have any idea how to make that.
Here is a functional example in JSFiddle.
If someone could help me step by step, for learn it, I would be very grateful. If panes are not the way to do this, or another way to do this better, please tell me how.
When user changes the selection of your drop down list, you first try to clear your map by removing your previously created custom panes from the DOM.
There is a very high chance Leaflet does not expect such fiddling with its panes, even if they are custom. That is probably why when you create new custom panes with the same names, Leaflet does not reference them correctly, hence it does not display your new layers in those new panes.
For adding and removing your layers dynamically to / from the map, you should simply use Layer Groups instead. In particular, it provides you with a clearLayers method that does what you are trying to achieve.
Therefore, you just add your polygons and markers to that Layer Group (instead of directly to the map), so that you can easily clear them when the user selects a new value. The custom panes remain unaffected.
// Initialize the Layer Group.
// Add it to map so that its children will appear on map.
var layerGroup = L.layerGroup().addTo(map);
// On <select> "change" event:
layerGroup.clearLayers();
// When you have retrieved your data and are ready to convert it to layers:
L.geoJSON(data, {
style: { // Can be a function as usual.
pane: 'polygon'
},
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, {
pane: 'POI'
});
}
}).addTo(layerGroup); // Add to layerGroup instead of directly to map.
Updated JSFiddle: https://jsfiddle.net/Ljetvd2x/4/
As for updating your legend, I am sure you should be able to find some resources on that topic, including here on SO. If you still need help, that should very probably be separated into a new post.

Adobe Animate CC - Reused Button Not Working HTML5

I am having an issue with reusing the same button within an HTML5 canvas. The button needs to be reused over the course of several separate frames as well as several times within the same frame.
The button works correctly with the first use:
this.button_13.addEventListener("click", fl_ClickToGoToAndStopAtFrame_24.bind(this));
function fl_ClickToGoToAndStopAtFrame_24()
{
this.gotoAndStop(72);
}
At frame 72, I reuse the same button symbol with a new instance name. Unfortunately, this button does not work:
this.button_14.addEventListener("click", fl_ClickToGoToAndStopAtFrame_25.bind(this));
function fl_ClickToGoToAndStopAtFrame_25()
{
this.gotoAndStop(78);
}
Clicking this second button shows the button states, but will not advance the user to frame 78.
*If the 2nd button exists on the timeline for frame 72 only, it is not present in the published result at all. If the 2nd button exists on frame 72-77, the button is present but not functional.
Any ideas?
Try to do this
this.button_13.removeEventListener("click", fl_ClickToGoToAndStopAtFrame_24.bind(this));
before creating the second event listener.
You have two options here.
Put your separate button instances on different layers.
Create one button instance on its own layer, have a script set the button's visible property (true/false) as needed, and modify your click handler to react based on the value of this.currentFrame.
Animate CC (2015.2 for me at the time of this writing) seems to generate its HTML 5 script output with animation in mind. When you have like object instances on multiple frames on the same layer, instead of creating a new instance of that object, it re-purposes the instance from a prior frame.
e.g. I create a dynamic text box named "myText" on frame 1. On frame 2, I create another dynamic text box and name it "myOtherText". When I publish and look at the script, the script calls Tween.to() first to place the "myText" object on the canvas and give it its appearance and position, then chains another .to() call to make myText look like myOtherText. Under the hood, myOtherText simply doesn't exist.
Creating another layer forces Animate to create the instances explicitly in CreateJS. However, if you have a lot of buttons, that could end up making a ton of layers and making your timeline look messy. I'd recommend option 1 if you only have a couple button instances, but for the latter case, option 2 may be the way to go.
Create a new layer. Name it whatever you want, like "button layer".
This layer will have only one keyframe.
Place your button instance on this layer. For continuity's sake, let's name it button_13.
Bind a tick handler to the root MovieClip (i.e. this) to handle whether the button should appear or not.
var tickHandler = function () {
switch (this.currentFrame) {
//Add case statements for each frame you want the button to appear on
//Remember, frame indexes in CJS are 0-based!
case 12: case 13:
this.button_13.visible = true;
break;
default:
this.button_13.visible = false;
}
}.bind(this);
this.removeEventListener('tick', tickHandler); //Make sure not to double-bind
this.addEventListener('tick', tickHandler);
Bind a click handler to the button that will gotoAndStop on a frame dependent on the current frame.
var clickHandler = function () {
switch (this.currentFrame) {
//Add case statements for each frame that should have a jump
//Remember, frame indexes in CJS are 0-based!
case 12:
this.gotoAndStop(72);
break;
case 13:
this.gotoAndStop(78);
break;
}
}.bind(this) //Proxy the handler, as done above.
this.button_13.removeEventListener('click', tickHandler); //Make sure not to double-bind
this.button_13.addEventListener('click', tickHandler);
Place both of those scripts onto frame 1 of your movie.
I haven't tested this myself as presented, but it should accomplish what you're looking for.
The solution is to move the instance on another layer. In that way, two different instances can be used in different images with different scripts and different names.

Limit Google Maps Drawing library to one Polyline/shape

I have somewhat of a unique problem. I'd like to allow a user to draw only one polyline and limit it to that. I'd also like that to be able to edit the polyline at will. I found this example: Google Maps Drawing Manager limit to 1 polygon but that basically just disables the control panel which still doesn't limit anything and secondly I want them to be able to edit their polyline, just not be able to create new ones.
I'd even be fine if they had some sort of a onDrawStart listener that would clear the previous shape but as far as I can tell, they don't have any such listener. Thanks!
Add a listener to overlaycomplete or polylinecomplete. Save the new layer. Then you can either delete the old one after the user draw another one..
google.maps.event.addListener(map.drawingManager, "overlaycomplete", function(event){
event.overlay.overlayType = event.type;
lastOverlay = event.overlay; // Save it
map.drawingManager.setDrawingMode(null); // Return to 'hand' mode
});
So, if you want to delete the layer => lastOverlay.setMap(null).
There is also a listener that fires when the user select a drawing tool. Mmm I can't find this listener. Not sure if it really exists.

Raphael.js how to find click on path's stroke?

How can I find out with Raphael.js (when the user click on a SVG path) the click was on the stroke?
Here is the path:
var polycoords = [['M',10,10],['L',30,10],['L',35,50],['L',5,45],['Z]];
var poly = paper.path(polycoords).attr({'stroke':'#00f','stroke-width':5});
poly.click(function(e) {
// I think here can I find it somehow...
});
As far as your path it closed but not filled with a color or a pattern, only the strokes are clickable, so the problem seems to resolve itself.
If in some not pasted code lines, you actually fill this path, then, also the inside is clickable and there is no difference in the event argument provided to the click callback.
Two ways:
having the stroke and the inside as two differents path (one filled and without strokes, the other not filled but with strokes) and attach seperate click handlers
on click, create a temporary path surrounding the pixel you clicked (whose coordinates are in the event argument), and search for intersections with the existing path with Raphael.pathIntersection(path1, path2) (RaphaelJS documentation: pathIntersection)

Categories

Resources