Other method to call layers by options? - leaflet

I have this script the loop through all layers and do some stuff when its layer's option integer is found.
L.marker([1, 2], {
divId: 1,
})
.addTo(map);
L.marker([2, 4], {
divId: 2,
})
.addTo(map);
map.eachLayer(function(layer) {
if(layer.options && layer.options.pane === "markerPane") {
var a = layer.options.divId;
var b = localStorage.getItem(saveID);
if (b == a) //check if marker's option divID is = 1
{
//do something
} else {
//do something
}
}
});
My question is, is there any method to call the layer with the option of divID:1? instead of looping each map layer

Related

Updating data doesnt expand the data tree inside material-table

Im trying to build a table with nested tree folder inside.
When trying to add nested data into the datasource data the structure will not updated and will not toggle anymore.
Code below:
https://stackblitz.com/edit/angular-table-tree-example-k2zqmt?file=app%2Ftable-basic-example.ts&file=app%2Ftable-basic-example.html,app%2Ftable-basic-example.ts
Environment
Angular:
Material Table
Material tree system
These are the things that are happening when logNode method is called
The item is getting added but the treeControl.toggle method does not work anymore.
When you are assigning a new dataset to the dataSource all the nodes get reset and the tree closes, so this.treeControl.toggle is trying to toggle a node that does not exist.
You need to find the node to be toggled from the list you get from treeControl.dataNodes
I would suggest having the toggle code in a separate method and adding a node code in a separate method, and a separate button to add the node.
The below code should work for your scenario, also remove this line from your HTML, (click)="treeControl.toggle(data)"
interface ExampleFlatNode {
expandable: boolean;
RoleName: string;
Access: boolean;
level: number;
CatId: number;
}
private transformer = (node: FoodNode, level: number) => {
return {
expandable:
!!node.CategoryPermissions && node.CategoryPermissions.length > 0,
RoleName: node.RoleName,
Access: node.Access,
level: level,
CatId: node.CatId,
};
};
tempNodes = []
constructor() {
this.dataSource.data = TREE_DATA;
}
logNode(clickedNode) {
this.tempNodes = [];
this.treeControl.dataNodes.forEach((node) =>
this.tempNodes.push({
...node,
expanded: this.treeControl.isExpanded(node),
})
);
if (!this.treeControl.isExpanded(clickedNode)) {
const temp = {
Access: true,
RoleName: 'test 1 2',
CatId: 113,
};
const clickedNodeIdx = this.treeControl.dataNodes.findIndex(
(node: any) =>
node.CatId === clickedNode.CatId &&
node.RoleName === clickedNode.RoleName &&
node.level === clickedNode.level
);
const childIdx = 1;
let child;
if (clickedNode.level === 0) {
child =
this.dataSource.data[clickedNodeIdx].CategoryPermissions[childIdx];
} else {
this.dataSource.data.forEach(
(item) => (child = this.findDataSource(item, clickedNode))
);
}
child.CategoryPermissions.push(temp);
this.dataSource.data = this.dataSource.data;
const addedNode = this.treeControl.dataNodes.find(
(node: any) =>
node.CatId === temp.CatId && node.RoleName === temp.RoleName
);
this.expandParent(addedNode);
this.setPreviousState();
} else {
this.treeControl.collapse(clickedNode);
}
}
findDataSource(item, node) {
if (item.RoleName === node.RoleName) {
return item;
} else if (item.CategoryPermissions) {
let matchedItem;
item.CategoryPermissions.forEach((e) => {
const temp = this.findDataSource(e, node);
if (temp) {
matchedItem = temp;
}
});
return matchedItem;
}
}
setPreviousState() {
for (let i = 0, j = 0; i < this.treeControl.dataNodes.length; i++) {
if (
this.tempNodes[j] &&
this.treeControl.dataNodes[i].RoleName === this.tempNodes[j].RoleName &&
this.treeControl.dataNodes[i].CatId === this.tempNodes[j].CatId &&
this.treeControl.dataNodes[i].level === this.tempNodes[j].level
) {
if (this.tempNodes[j].expanded) {
this.treeControl.expand(this.treeControl.dataNodes[i]);
}
j++;
}
}
}
expandParent(node: ExampleFlatNode) {
const { treeControl } = this;
const currentLevel = treeControl.getLevel(node);
const index = treeControl.dataNodes.indexOf(node) - 1;
for (let i = index; i >= 0; i--) {
const currentNode = treeControl.dataNodes[i];
if (currentLevel === 0) {
this.treeControl.expand(currentNode);
return null;
}
if (treeControl.getLevel(currentNode) < currentLevel) {
this.treeControl.expand(currentNode);
this.expandParent(currentNode);
break;
}
}
}

w2ui filter option "contains not" possible?

I am using w2ui (1.5) and I would be interested in whether it is possible to use a filter that only delivers negative results
That means only records/keys which not fullfilling a certain criteria.
Like a condition "contains not" or "is not" in addition to
http://w2ui.com/web/docs/1.5/w2grid.textSearch.
Thanks!
Gordon
okay, a possible solution is
w2ui['grid'].search([{ field: var1, value: var2, operator: 'not in'}], 'OR');
I coded my own solution to this problem. This adds a way to use "not" for string and "!=" for number searches.
This function does the search and it is also used to store the grid advanced search popup in history state.
I'm sure this can be even more optimized, so please use this more like a guideline. Hope this helps somebody.
function searchExtend(event, grid) {
// if event is null, we do just the local search
var searchObj;
if (event == null) {
searchObj = grid;
} else {
searchObj = event;
}
// make a copy of old data
const oldSearchData = structuredClone(searchObj.searchData);
const oldSearchLogic = structuredClone(searchObj.searchLogic);
var searchData = searchObj.searchData;
var invertedSdata = [];
var toSplice = [];
// check operator if it's "not" or "!="
for (var i = 0; i < searchData.length; i++) {
var sdata = searchData[i];
// invert the condition
if (sdata.operator == "not") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "contains",
value: sdata.value
});
}
if (sdata.operator == "!=") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "=",
value: sdata.value
});
}
}
// remove all "not" and "!=" from searchData
for (var i in toSplice) {
searchData.splice(i, 1);
}
var foundIds = [];
// use inverted criteria to search
if (invertedSdata.length > 0) {
grid.searchData = invertedSdata;
grid.searchLogic = "OR";
grid.localSearch();
grid.searchLogic = oldSearchLogic;
// store found ids
foundIds = structuredClone(grid.last.searchIds);
}
if (foundIds.length > 0) {
// perform a search with original criteria - spliced "not" and "!="
grid.searchData = searchData;
grid.localSearch();
var allRecIds = structuredClone(grid.last.searchIds);
// if there's not any results, push push all recIds
if (grid.last.searchIds.length == 0) {
for (let i = 0; i < grid.records.length; i++) {
allRecIds.push(i);
}
}
// remove all ids found with inverted criteria from results. This way we do the "not" search
for (const id of foundIds) {
allRecIds.splice(allRecIds.indexOf(id), 1);
}
if (event != null) {
// let the search finish, then refresh grid
event.onComplete = function() {
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
} else {
// refresh the grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
return;
}
if (event != null) {
event.onComplete = function() {
setSearchState(grid); // store state
}
} else {
// refresh whole grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
}
function refreshGrid(grid, allRecIds, oldSearchData) {
grid.last.searchIds = allRecIds;
grid.total = grid.last.searchIds.length;
grid.searchData = oldSearchData;
grid.refresh();
}
function setSearchState(grid) {
history.replaceState(JSON.stringify(grid.searchData), "Search");
}
To use this, you have to call it from the grid's onSearch:
onSearch: function(event) {
searchExtend(event, w2ui["grid"]);
}
Also, if you want to use the history.state feature, it needs to be called from onLoad function:
onLoad: function(event) {
event.onComplete = function() {
console.log("History state: " + history.state);
if (history.state != null) {
w2ui["grid"].searchData = JSON.parse(history.state);
searchExtend(null, w2ui["grid"]);
}
}
To add operators, please use this reference.
This is my solution to the problem:
operators: {
'text': ['is', 'begins', 'contains', 'ends', 'not'], // could have "in" and "not in"
'number': ['=', 'between', '>', '<', '>=', '<=', '!='],
'date': ['is', 'between', {
oper: 'less',
text: 'before'
}, {
oper: 'more',
text: 'after'
}],
'list': ['is'],
'hex': ['is', 'between'],
'color': ['is', 'begins', 'contains', 'ends'],
'enum': ['in', 'not in']
}

Ionic 2 search bar

I'm implementig a search bar, it filters the way I want, but after 2 seconds, it shows the whole array again, and I dont really understand why.
Thanks for your help.
This is the .ts
getCatalog() {
this.http.get('url', {}, {}).then(data => {
console.log("Data:", JSON.parse(data.data));
this.catalogList = JSON.parse(data.data);
// console.log(data.status);
// console.log(data.data); // data received by server
// console.log(data.headers);
})
.catch(error => {
console.log(error.status);
console.log(error.error); // error message as string
console.log(error.headers);
});
}
getItems(ev: any) {
// Reset items back to all of the items
this.getCatalog();
// set val to the value of the searchbar
let val = ev.target.value;
//console.log("VALUE", ev);
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.catalogList = this.catalogList.filter((item) => {
console.log("ITEM", item)
return (item.name.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
}
}
This is where i have the *ngFor
<ion-searchbar (ionInput)="getItems($event)"></ion-searchbar>
<ion-grid>
<ion-row *ngFor="let item of catalogList">
Other code here
I think the problem is that your HTTP request finishs after you've already filtered your array, this'll make the catalogList receive the parsed JSON after you've filtered, this is why it resets.
Do you really need to get your catalog from the server every time the ser types something in the searchbar? Is your catalog that dynamic? If not you can simply save it in your catalogList when the user enters the page and create another object to be used at your filter:
public catalogListFiltered: any[] = []; // CREATE A NEW VARIABLE THAT'LL BE USED ON YOUR NGFOR
ionViewDidEnter() { // or ionViewDidLoad, depends on what lifecycle you need
this.http.get('url', {}, {}).then(data => {
this.catalogList = JSON.parse(data.data);
this.initializeCatalogs();
});
}
// THIS'LL SET YOUR FILTERED ARRAY TO THE FIRST/FULL VERSION OF YOUR CATALOG
initializeCatalogs(){
this.catalogListFiltered = this.catalogList;
}
getItems(ev: any) {
// Reset items back to all of the items
this.initializeCatalogs();
// set val to the value of the searchbar
let val = ev.target.value;
//console.log("VALUE", ev);
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.catalogList = this.catalogList.filter((item) => {
console.log("ITEM", item)
return (item.name.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
}
}
If you really need to call your API everytime to get your catalog just work with promises
getCatalog = (): Promise<any> {
return new Promise<any>(resolve => {
this.http.get('url', {}, {}).then(data => {
resolve(JSON.parse(data.data));
});
});
}
// maybe this'll have the same effect as the above, maybe someone can say if that'll work with some changes on your 'getItem' method:
// getCatalog(){ return this.http.get('url', {}, {}); };
getItems(ev: any) {
// Reset items back to all of the items
this.getCatalog().then(res => {
this.catalogList = res;
// set val to the value of the searchbar
let val = ev.target.value;
//console.log("VALUE", ev);
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.catalogList = this.catalogList.filter((item) => {
return (item.name.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
}
});
}
Hope this helps.

How can I customize the 'home' and 'end' key navigation on ag-Grid?

I've customized the arrow navigation keys using the callback 'navigateToNextCell'.
But now I want to customize 'Home' and 'End' key.
There is any way to do it?
It's not super easy at the moment... here are some relevant "enhancements" that have been requested on the github page:
support page up/ down, home and end keys
allow overriding of keyboard events
Suggestion: Allow any key for navigation (not just tab)
That last link has something useful. There is a comment on how to disable any propagation of the 'home' and 'end' keys:
// note, this is angular 2 code, `this.el.nativeElement` is just the grid component
document.documentElement.addEventListener(
'keydown',
(e: KeyboardEvent) => {
// this runs on the document element, so check that we're in the grid
if (this.el.nativeElement.contains(e.target)) {
if (e.key === 'Tab' || e.key === 'Home' || e.key === 'End') {
e.stopImmediatePropagation();
e.stopPropagation();
}
if (e.key === 'Home' || e.key === 'End') {
// we don't want to prevent the default tab behaviour
e.preventDefault();
}
}
},
true
);
AFTER you have done that, you could add new event listeners to the grid, or depending on what you are trying to do, you could add listeners to specific cells as it mentions in the Keyboard Navigation section of the Ag-Grid docs under Custom Actions:
Custom Actions
Custom cell renderers can listen to key presses on the focused div.
The grid element that receives the focus is provided to the cell
renderers via the eGridCell parameter. You can add your own listeners
to this cell. Via this method you can listen to any key press and do
your own action on the cell eg hitting 'x' may execute a command in
your application for that cell.
I initially wrote this code for navigating home/end with command+arrow key, but have put comments where logic differs since 98% will be the same. Comments are untested but should work
I will divide this into 4 parts:
suppress the event so ag-grid does nothing by default
find a way for us to place a event listener on the right keys
compute which cell should be navigated to next
tell ag grid which cell should be selected
For #1, we can use suppressKeyboardEvent and either set it on defaultColDef(in `GridOptions´) or on every col def.
Mine looks like this:
const suppressKeyboardEventFn: ColDef["suppressKeyboardEvent"] = (
params: SuppressKeyboardEventParams
) => {
const key = params.event.key;
const isControl = isControlKey(params.event);
// For end/home, test for key === "Home" || key === "End" instead
const isNavigation =
key === "ArrowUp" ||
key === "ArrowDown" ||
key === "ArrowRight" ||
key === "ArrowLeft";
if (isNavigation && isControl) {
return true;
}
return false;
};
isControlKey is so it works on both mac&windows, and is written as recommended by MDN (not relevant if using home/end key):
const isMac = navigator.platform.startsWith("Mac");
export const isControlKey = (event: KeyboardEvent) => {
return isMac ? event.metaKey : event.ctrlKey;
};
For #2 where we want to handle the event ourselves, we can use onCellKeyDown:
const onCellKeyDownFn: GridOptions["onCellKeyDown"] = (cellKeyDown) => {
if (cellKeyDown.event == null) {
return;
}
const event = cellKeyDown.event as KeyboardEvent;
const key = event.key;
const isControl = isControlKey(event);
// Instead write cases here for case "Home": and case "End":
switch (key) {
case "ArrowUp": {
if (isControl) {
dispatch(navigateCell({ direction: "up", end: true }));
}
break;
}
case "ArrowDown": {
if (isControl) {
dispatch(navigateCell({ direction: "down", end: true }));
}
break;
}
case "ArrowRight": {
if (isControl) {
dispatch(navigateCell({ direction: "right", end: true }));
}
break;
}
case "ArrowLeft": {
if (isControl) {
dispatch(navigateCell({ direction: "left", end: true }));
}
break;
}
}
};
Using the same isControlKey function. I have implemented redux navigation actions that handles navigating to home and end.
For #3, we can implement this in different ways. Here is how I FIND the correct cell to navigate to. Either one step to a direction, or to the end/home (which is our scenario, since I pass end: true)
// For using Home/End, add a case for each and mix logic from
// below to find the correct cell. Last column logic is in
// right when end===true and last row is in down when end===true etc
switch (params.direction) {
case "up": {
let newRowIndex;
if (params.end === true) {
newRowIndex = 0;
} else {
newRowIndex = selectedCell.lastKnownRowIndex - 1;
if (newRowIndex < 0) {
return;
}
}
const node = gridApi.getDisplayedRowAtIndex(newRowIndex);
if (node?.id == null) {
log.warn("Could not find Node ID");
return;
}
dispatch(
selectCell({
nodeId: node.id,
colKey: selectedCell.colKey,
})
);
break;
}
case "down": {
let newRowIndex;
if (params.end === true) {
newRowIndex = maxRowIndex;
} else {
newRowIndex = selectedCell.lastKnownRowIndex + 1;
if (newRowIndex > maxRowIndex) {
return;
}
}
const node = gridApi.getDisplayedRowAtIndex(newRowIndex);
if (node?.id == null) {
log.warn("Could not find Node ID");
return;
}
dispatch(
selectCell({
nodeId: node.id,
colKey: selectedCell.colKey,
})
);
break;
}
case "right": {
let nextColumn;
if (params.end === true) {
const columns = columnApi.getAllGridColumns();
nextColumn = columns[columns.length - 1];
} else {
const column = columnApi.getColumn(selectedCell.colKey);
if (column == null) {
log.warn("Could not find column");
return;
}
nextColumn = columnApi.getDisplayedColAfter(column);
}
if (nextColumn == null) {
return;
}
dispatch(
selectCell({
nodeId: selectedCell.nodeId,
colKey: nextColumn.getColId(),
})
);
break;
}
case "left": {
let nextColumn;
if (params.end === true) {
const columns = columnApi.getAllGridColumns();
nextColumn = columns[1];
} else {
const column = columnApi.getColumn(selectedCell.colKey);
if (column == null) {
log.warn("Could not find column");
return;
}
nextColumn = columnApi.getDisplayedColBefore(column);
}
if (nextColumn == null) {
return;
}
dispatch(
selectCell({
nodeId: selectedCell.nodeId,
colKey: nextColumn.getColId(),
})
);
break;
}
default: {
assertNever(params.direction);
}
}
Lastly #4, here is parts of my selectCell implementation that can give you some idea of how to make sure ag-grid focuses the right cell.
This code will focus the cell, add a range highlight, add a row selection, and make sure that it is visible if the viewport needs to jump:
// no difference in logic when using Home/End!
// just make sure correct cell is passed
gridApi.ensureNodeVisible(node);
gridApi.ensureColumnVisible(params.colKey);
gridApi.clearRangeSelection();
gridApi.addCellRange({
rowStartIndex: rowIndex,
rowEndIndex: rowIndex,
columns: [params.colKey],
});
gridApi.setFocusedCell(rowIndex, params.colKey);
node.setSelected(true, true)

d3.js : very simple mouse interaction

I wan't to do a very simple chart with mouse interaction.
Example (work in progress) is on this page : http://velo70.ouvaton.org/2013/gpxvtt-un-nouveau-plugin/
The goal is : when you change the slider position, a circle have the same position on the chart. On the map, it's already done :)
The best issue could be : when you move the slide, the circle-chart move, and when you move the circle on the chart, the slide move too... But maybe too hard for me :\
Any link with tutorial to progress with that ?
Thanks.
One way is to bind circles to every data point in your graph and then set the display: none to all but the one that corresponds to your active slider position.
Add the circles after you append path.lineSup:
chart.selectAll("circle.highlightPoint")
.data(data)
.enter()
.append("circle")
.attr("class", "highlightPoint")
.attr("fill", "pink")
.attr("cx", function(d) { return x(d.dist); })
.attr("cy", function(d) { return y(d.ele); })
.attr("display", "none");
Add to your slider function:
d3.selectAll("circle.highlightPoint")
.attr("display", function(d,i) { return i == id ? "block" : "none"});
I think that should work.
Thanks. Good and pedagogic way for me :)
I finally test with
chart.append("circle")
.data(data)
.attr("class", "highlightPoint")
.attr("r",4)
.attr("innerRadius",0)
.style("fill", "pink")
.style("stroke","blue")
.style("stroke-width",1)
.attr("cx", function(d) { return x(d.dist); })
.attr("cy", function(d) { return y(d.ele); })
.attr("display","block") ;
But it display only the first circle.
I don't really understand the function(d) and function(d,i) use :\
Test in http://velo70.ouvaton.org/2013/gpxvtt-un-nouveau-plugin/
It's a little late.. but i need participation points.. :)
For simple handle click mouse and call function on event, I give you a complete file to link with simple function for d3.js..
<script src="js/file.js"></script>
And "THE" File to include ( mainly analyse it for 'on') :
$(function() {
// Handler for .ready() called.
var data = [],
width = 300,
height = 300,
// An array to hold the coordinates
// of the line drawn on each svg.
coords = [],
line = d3.svg.line(),
// Set the behavior for each part
// of the drag.
drag = d3.behavior.drag()
.on("dragstart", function() {
// Empty the coords array.
coords = [];
svg = d3.select(this);
// If a selection line already exists,
// remove it.
//svg.select(".selection").remove();
// Add a new selection line.
svg.append("path").attr({"class": "selection"});
})
.on("drag", function() {
// Store the mouse's current position
coords.push(d3.mouse(this));
svg = d3.select(this);
// Change the path of the selection line
// to represent the area where the mouse
// has been dragged.
svg.select(".selection").attr({
d: line(coords)
});
// Figure out which dots are inside the
// drawn path and highlight them.
selected = [];
svg.selectAll("circle.dot").each(function(d, i) {
point = [d3.select(this).attr("cx"), d3.select(this).attr("cy")];
if (pointInPolygon(point, coords)) {
selected.push(d.id);
}
});
highlight(selected);
})
.on("dragend", function() {
svg = d3.select(this);
// If the user clicks without having
// drawn a path, remove any paths
// that were drawn previously.
if (coords.length === 0) {
// d3.selectAll("svg path").remove();
unhighlight();
return;
}
// Draw a path between the first point
// and the last point, to close the path.
svg.append("line").attr({
"class": "terminator",
d: line([coords[0], coords[coords.length-1]])
});
});
function randomPoint() {
return Math.floor(Math.random()*(width-30)) + 20;
}
// from https://github.com/substack/point-in-polygon
function pointInPolygon (point, vs) {
var xi, xj, i, intersect,
x = point[0],
y = point[1],
inside = false;
for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
xi = vs[i][0],
yi = vs[i][1],
xj = vs[j][0],
yj = vs[j][1],
intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
}
function unhighlight() {
d3.selectAll("circle.dot").classed("highlighted", false);
}
function highlight(ids) {
// First unhighlight all the circles.
unhighlight();
// Find the circles that have an id
// in the array of ids given, and
// highlight those.
d3.selectAll("circle.dot").filter(function(d, i) {
return ids.indexOf(d.id) > -1;
})
.classed("highlighted", true);
}
function Scatter(data, selector, group) {
var svg = d3.select(selector).append("svg")
.attr({
width: width,
height: height
}).call(drag),
g = svg.append("g").attr({"class": "g-dot"}),
// Create a circle element for each
// item in the data passed.
dot = g.selectAll("circle.dot")
.data(data)
.enter().append("circle")
.attr({
"class": "dot",
r: 8,
cx: function(d, i) {
return d[group].x;
},
cy: function(d, i) {
return d[group].y;
},
})
.on("mouseover", function(d, i) {
// Highlight circles on mouseover, but
// only if a path hasn't been drawn.
if (d3.selectAll("svg path").empty()) {
highlight([d.id]);
}
})
.on("mouseout", function(d, i) {
// If a path hasn't been drawn,
// unhighlight the highlighted circles.
if (d3.selectAll("svg path").empty()) {
unhighlight();
}
});
text = g.selectAll("text")
.data(data)
.enter().append("text")
.attr({
x: function(d, i) {
return d[group].x;
},
y: function(d, i) {
return d[group].y + 4;
}
})
.text(function(d, i) {
return d.id;
});
}
// Add the dots to each canvas div.
Scatter(data, "#tableau_principal", "a");
Scatter(data, "#canvas2", "b");
Scatter(data, "#canvas3", "c");
}); // // FIN DOC READY //
// functions generales used partt //