How to change base layer using JS and leaflet layers control - controls

I have to modify existing application, where leaflet layers control is used - I need to display one of the base layers when the map is initiated. Is there a way, how to call some function from the layers control from JS script - something like control.select(1) ....? If not, how can add a tile layer in the same way as it is done by the control - when I add new L.TileLayer during map init, it's not overwritten by manual layers control selection change?

You could try to emulate a user click on the Leaflet Layers Control, but there is a much more simple way to achieve what you initially describe.
Normally by simply adding a layer to the map (e.g. myTileLayer.addTo(map)), if that layer is part of the base layers or overlays of the Layers Control, the latter will automatically update its status (if you added a base layer, the radio buttons will be selected accordingly; for an overlay, the corresponding checkbox will be ticked).
Now I am not sure I understood properly your last part ("when I add new L.TileLayer during map init, it's not overwritten by manual layers control selection change").
If you mean you have an unexpected behaviour because the Tile Layer you added is not changed by the Layers Control, it may be due to the fact that you are not re-using a Tile Layer that the Layers Control knows: do not use new L.TileLayer, but re-use one of the base layers or overlays.
For example:
var baselayers = {
"Tile Layer 1": L.tileLayer(/* ... */),
"Tile Layer 2": L.tileLayer(/* ... */),
"Tile Layer 3": L.tileLayer(/* ... */)
};
var overlays = {};
L.control.layers(baselayers, overlays).addTo(map);
baseLayers["Tile Layer 1"].addTo(map);

There are several ways to handle this problem.
1) You can select second baselayer by clicking on the radio input in layer control. This can be done programatically like this (not recommended):
var layerControlElement = document.getElementsByClassName('leaflet-control-layers')[0];
layerControlElement.getElementsByTagName('input')[1].click();
2) Just change the order of baseLayers passed into L.Control.Layers during initialization.
3) Extend L.Control.Layers so that it accepts some new option {"selectedBaseLayerIndex": 1}

I found this after digging in the leaflet code:
1) find the layer you want to display in control's structure _layers
2) call map.addLayer(_layers[your_layer_index].layer)
3) find your layer in control's structure _form
4) set it's checked attribute to true

Thank you ghybs. You help me to understand leaflet.
I keep base-map preference in FireBase and get it back on connection to store via Redux.
Now my Map component re-render with tileLayer from Redux.
Before I tried to pass it on props... But with leaflet, like ghybs says, you have to add it again to the map, even if you gave it with something like :
const mapRef = useRef(); //Useful to reach Map leaflet element
layerRef.current = L.control
.layers(baseMaps, null, { position: "topleft", sortLayers: true})
.addTo(map);
And after, I hook my tileLayer :
useEffect(() => {
const { leafletElement: map } = mapRef.current; //Don't forget the current...
baseMaps[tileLayer].addTo(map);
}, [tileLayer]);
return (
<Map
onbaselayerchange={(ev) => handleBaseLayerChange(ev.name)}
layers={defaultLayer(tileLayer)}
ref={mapRef}
{...fieldProps}>
<CustomersMarkers layer={layerRef} customers={customers} />
</Map>
);

If you are using jQuery you can simulate a click on the Layers control to change the base layer:
$('.leaflet-control-layers-selector')[0].click()
If you want to switch to the second map layer use the next index [1]

Related

Adding symbol layer with one source removed FillLayer with another source - Mapbox android

I have ploygons which I have drawn using FillLayer mapbox which uses a set of GeoJson coordinates to create a source and add to the Fill layer.
After that I am creating another layer (Symbol Layer) this time with a different set of coordinates creating another source object.
When I add Symbol layer,the previously added Fill layer disappears.
I tried multiple solutions but its not working.
Is it even possible to add multiple layers of different types with different source ids?
var bm = BitmapFactory.decodeResource(
resources,
com.mapbox.services.android.navigation.ui.v5.R.drawable.mapbox_marker_icon_default
)
// bm = Bitmap.createScaledBitmap(bm, 80, 80, true);
style.addImage("ID_ICON_AIRPORT", bm)
// Create a SymbolManager.
val symbolManager = SymbolManager(mapView, map, style)
// Set non-data-driven properties.
symbolManager.iconAllowOverlap = true
//symbolManager.iconIgnorePlacement = true
// Create a symbol at the specified location.
val options =
SymbolOptions()
.withLatLng(LatLng(latitude, longitude))
.withIconImage("ID_ICON_AIRPORT")
.withIconSize(1.3f).withTextSize(2f)
symbolManager?.create(options)
symbolManager.addClickListener {
// to open infowindow here
}
I tried with SymbolManager , it works to display the marker but now I don't know how to add info window on the marker click. Expected to see both symbol layer and fill layer, I want symbol layer on top of fill layer.

Update grid state in Angular-Slickgrid after grid creation

What is the best way to set a saved grid state after the angular-slickgrid has already been created? The Grid State/Presets - Wiki explains setting the saved state on load by setting the gridOptions.presets. In my case, I would like to update the grid state when the underlying saved state has changed in local storage (perhaps saved from another instantiation of the app), and apply the state to the current slickgrid. If I update the gridOptions.presets, is there a method I can call to force the grid to update with the new presets?
Please note that I'm the author of Angular-Slickgrid.
The answer is No it's called Presets for a reason, it only works when creating the grid...but you can still do it with a few method calls. So if you really wanted to use the Grid State then you'll have to save it yourself and then reload the entire grid after applying all previous State. The Grid State that can be applied dynamically are the Filters and Sorting which you can see in Example 4 and Example 25 (with a button click or a dropdown selection like the last example). I did later add a method to change the columns as well and that one is demoed under this Slickgrid-Universal Example 11, in fact that demo will show you exactly the way you want to do it, you can follow the code here.
for a short code sample, you'll need to get the angularGrid instance from (onAngularGridCreated) and then use it to dynamically change the grid. It shows you all the options, you can skip any of them if you don't need or want to change that option.
angularGridReady(angularGrid: AngularGridInstance) {
this.angularGrid = angularGrid;
}
// the `selectedView` should be the result of your Grid State
changeGridView(selectedView: GridState) {
if (selectedView) {
const columns = selectedView?.columns ?? [];
const filters = selectedView?.filters ?? [];
const sorters = selectedView?.sorters ?? [];
this.angularGrid.filterService.updateFilters(filters as CurrentFilter[]);
this.angularGrid.sortService.updateSorting(sorters as CurrentSorter[]);
this.angularGrid.gridStateService.changeColumnsArrangement(columns);
// if you have a frozen grid (pinning)
this.angularGrid.gridService.setPinning(pinning);
} else {
// to reset the grid
this.angularGrid.filterService.clearFilters();
this.angularGrid.sortService.clearSorting(); this.angularGrid.gridStateService.changeColumnsArrangement([...this.columnDefinitions].map(col => ({ columnId: `${col.id}` })));
// if you have a frozen grid (pinning)
this.angularGrid.gridService.clearPinning();
}
// you might want to scroll back to top of the grid if filters are changed
this.angularGrid.slickGrid.scrollColumnIntoView(0);
}
You might not need to re-render the grid but in case the grid UI doesn't show correctly, you could force a re-render of the grid by invalidating all its rows
this.angularGrid.slickGrid.invalidate();

Create PySimpleGUI list with a key

I wish to have a list of text items in a PySimpleGUI that I can update later. That is, I want to have a key for the list. This might be vertical or horizontal, and I do not know how many items there will be.
I end up with different use cases, but the current one is to make a single line of text items with different colors. Other times, I need to write and update a customized table, just different enough that the table widget does not work.
Conceptually, I want to do something like this:
layout = [ [sg.T('Titles and Things')], sg.ListThing(key='-data-', [[]]) ]
so that I can:
window['-data-'].update(values=[ [sg.T(v, color=c)] for (v,c) in my_data ])
Another, invalid syntax, way of saying what I want is to use [key="-data-", sg.T('Item1'), sg.T('Item2')].
Is this possible?
You can update individual layout elements but you cannot dynamically change the layout itself.
It is possible to create 2 or more elements, whereby only one of them is visible, and switch them later as needed. Or you can close and re-create the window with another layout. Or combine both approaches.
An example of switching layouts:
def change_layout():
left_col_1 = sg.Column([[sg.Text(f'Text {i}') for i in range(4)]], visible=True, key='col_1')
left_col_2 = sg.Column([[sg.Text(f'Text {i}')] for i in range(6)], visible=False, key='col_2')
visible_1 = True
layout = [[sg.Column([[left_col_1, left_col_2]]), sg.Button('Change layout', key='change')]]
window = sg.Window('window', layout=layout, finalize=True)
while True:
event, values = window.read()
print(event)
print(values)
print(visible_1)
if event in ('Exit', sg.WIN_CLOSED):
break
if event == 'change':
window['col_1'].update(visible=not visible_1)
window['col_2'].update(visible=visible_1)
visible_1 = not visible_1
Please notice that the alternative layouts for the left part (left_col_1, left_col_2) need to be enclosed in a container (column, frame) to keep their position in the window in the moment they are invisible.

can't set a property for flex container in segment widgets

I am new to Kony framework. Now i am going through Widget. There i came across Segment widgets using I would create a flex container with some labels and textbox.
My UI design are :
1. I Created a segment and set a flex container with some labels and text box in that segment
2. After that I turn off the flex container visible
3. And I type a code like :
function flex()
{
frmAssign.sgmt1.flex1.isVisible = true;//to show flex as visible but it does not read the property of that flex
}
In simple terms just If I click segment first row flex container isVisible should be true
enter image description here
want to achieve this design in kony
Try change frmAssign.sgmt1.flex1.isVisible = true;
frmAssign.sgmt1.flex1.setVisibility(true);
You cannot access the widget of segment directly.
You have to create a property (eg:isVisible) in Master Data of the segment.
initial Value of this property would be "false",
Then change the value as you per your need.
To change properties in segment data you have change the properties in array which you have set to data of segment.
Basically idea is
if you are using masterdata then you need to read the data change
property values and reassign.
if you are dynamically setting data
then you need to change that array and reassign
// always check for Null for selecteindex
//Note keep your existing properties and just change isVisible to true
var selecteindex= frmAssign.sgmt1.selectedRowIndex;
var segData = frmAssign.sgmt1.data[selecteindex];
segData[selecteindex] =("YourFlexName": {
"text": "CButton1",
"isVisible":true
});
form1.segment1.setDataAt(segData,selecteindex);
The right way to do this is :
var selectedIndex= frmAssign.sgmt1.selectedRowIndex;
var rowData = frmAssign.sgmt1.data[selectedIndex];
rowData["flex1"]["isVisible"] = true;
form1.segment1.setDataAt(rowData, selectedIndex);

WP7 Bing Maps Zoom level based on Push Pin collection locations

Just a quick question regarding the use of the following code snippet:
var locations = CurrentItems.Select(model => model.Location);
map.SetView(LocationRect.CreateLocationRect(locations));
as suggested in this answer:
Zoom to show all locations in bing maps
I am retrieving a list of geocoordinate asynchrounsly and binding these to a Bing Map using an ObservableCollection; copying the resultant data over to the main UI thread using:
Deployment.Current.Dispatcher.BeginInvoke( ()=> {...} )
My problem is that, I can't reference the map control within the Dispatcher (or can I??), so how can I apply the new Pushpin locations to the map using:
map.SetView(LocationRect.CreateLocationRect(locations));
Thanks,
S.
Because Map ultimately derives from DependencyObject it actually has its own Dispatcher. As such you can do;
map.Dispatcher.BeginInvoke(() => map.SetView(LocationRect.CreateLocationRect(locations)));
Also, it's worth noting you only need to call BeginInvoke() if the CheckAccess() returns false. (CheckAccess is tagged with an EditorBrowsable(EditorBrowsableState.Never) attribute so it won't show up in intellisense, you'll have to type it manually). The common pattern is;
if (map.Dispatcher.CheckAccess() == false) {
map.Dispatcher.BeginInvoke(() => map.setView(LocationRect.CreateLocationRect(locations)));
} else {
map.SetView(LocationRect.CreateLocationRect(locations));
}
I perhaps you will find this post useful. To bind the view of the map and the ViewModel, the method described use a DependecyPropety : http://sveiberg.wordpress.com/2012/06/24/5/.

Resources