Code efficiency using VectorGrid in Leaflet - javascript

I have about 7 000 polygons in a GeoJSON file using VectorGrid, all is fine using one layer but I need to split this layer into 10 LayerGroups (10 regions with their own polygons). How can this be done without rewriting the code 10 times? That seems to be lots of waste, there must be a smarter way and I can't figure it out. This is the code Im testing with, the highlight has to be working with all 11 layers...
var all_regions = new L.layerGroup();
var region_1 = new L.layerGroup();
var region_2 = new L.layerGroup();
var region_3 = new L.layerGroup();
/* snip */
var region_10 = new L.layerGroup();
var highlight_polygon;
var clearHighlight = function () {
if (highlight_polygon) {
vectorGrid.resetFeatureStyle(highlight_polygon);
}
highlight_polygon = null;
};
var vectorTileOptions_allRegions = {
rendererFactory: L.canvas.tile,
maxNativeZoom: 13,
zIndex: 6,
vectorTileLayerStyles: {
sliced: {
weight: 2,
color: "gray",
opacity: 1,
fill: false,
//fillColor: 'white',
//stroke: true,
fillOpacity: 0,
},
},
interactive: true,
getFeatureId: function (f) {
return f.properties.id;
},
};
var vectorTileOptions_region_1 = {
rendererFactory: L.canvas.tile,
maxNativeZoom: 13,
zIndex: 6,
vectorTileLayerStyles: {
sliced: function (properties, zoom) {
var region = properties.region;
if (region === "region one") {
return {
weight: 2,
color: "gray",
opacity: 1,
fill: false,
//fillColor: 'white',
//stroke: true,
fillOpacity: 0,
};
} else {
return {
weight: 0,
opacity: 0,
fill: false,
stroke: false,
fillOpacity: 0,
interactive: false,
};
}
},
},
interactive: true,
getFeatureId: function (f) {
return f.properties.id;
},
};
// Next vectorTileOptions until all 11 of them....
$.getJSON("/data/regions.geojson", function (json) {
//Not sure this is the correct way doing it...
var vectorGrid = L.vectorGrid
.slicer(json, vectorTileOptions_allRegions, vectorTileOptions_region_1)
.on("click", function (e) {
var properties = e.layer.properties;
L.popup()
.setContent(
"<b>Name</b>: " +
properties.region_name +
"<br><b>Date</b>: " +
"<i>" +
properties.date +
"</i>"
)
.setLatLng(e.latlng)
.openOn(map);
clearHighlight();
highlight_polygon = e.layer.properties.id;
vectorGrid.setFeatureStyle(highlight_polygon, {
weight: 3,
color: "gray",
opacity: 1,
fillColor: "#ff9999",
fill: true,
radius: 6,
fillOpacity: 0.3,
});
L.DomEvent.stop(e);
});
var clearHighlight = function () {
if (highlight_polygon) {
vectorGrid.resetFeatureStyle(highlight_polygon);
}
highlight_polygon = null;
map.on("popupclose", clearHighlight);
};
//This will not work....
vectorGrid.addTo(all_regions);
vectorGrid.addTo(region_1);
});

You probably want to do something like...
var regions = []; // An array that will hold instances of VectorGrid
var vectorGridOptions = {
rendererFactory: L.canvas.tile,
maxNativeZoom: 13,
zIndex: 6,
vectorTileLayerStyles: {
sliced: {}, // Empty, because it shall be overwritten later.
},
};
var defaultStyle = {
stroke: true,
weight: 2,
};
var regionStyles = [];
regionStyles[0] = {
weight: 2,
color: "gray",
};
regionStyles[1] = {
weight: 1,
color: "red",
};
/* ...etc, up to regionStyles[9] */
fetch("/data/regions.geojson")
.then(function (response) { return response.json(); })
.then(function (json) {
// For each number between 0 and 9...
for (var i = 0; i <= 9; i++) {
// Assuming that the GeoJSON data holds a FeatureCollection,
// create a copy of said GeoJSON FeatureCollection, but holding only
// the wanted features.
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
var filteredGeoJSON = {
type: "FeatureCollection",
features: json.features.filter(function (feature) {
// This assumes that each Feature has a "regionID" property with a
// numeric value between 0 and 9.
return feature.properties.regionID === i;
}),
};
// Build up the options for the i-th VectorGrid by merging stuff together.
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
var fullRegionStyle = Object.assign({}, defaultStyle, regionStyles[i]);
// Overwrite some stuff in vectorGridOptions. Note that this changes the value of
// a piece of vectorGridOptions at each pass of the loop.
vectorGridOptions.vectorTileLayerStyles.sliced = fullRegionStyle;
regions[i] = L.vectorGrid.slicer(filteredGeoJSON, vectorTileOptions);
regions[i].addTo(map);
}
});
The key points here are:
Use a loop to iterate from 1 through 10
Keep things in numbered arrays instead of similarly-named variables
Filter the FeatureCollection, so each VectorGrid works with less data. Drawing invisible polygons/polylines would take as much computing time as drawing visible ones.
Refactor as much as possible, then build up concrete data structures (Object.assign, clone objects if needed)

Related

Cesium polygon callback using javascript

I am trying to edit the change the edit or redraw the polygon. this is my code.
Error
An error occurred while rendering. Rendering has stopped.
TypeError: this._callback is not a function
TypeError: this._callback is not a function
using pickedObject.id i got the exect polygon i want to reposition, but call back issue.
var points = [-95.8079865631313, 30.24038650541154, -
60.10509002138564, 23.526593580490083, -59.06372427570612, 2.245934026097194, -
117.00668212362282, 3.938434130034481
];
function loadPoly(points) {
redPolygon = viewer.entities.add({
id: "myArray",
name: "myArray",
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray(points),
material: Cesium.Color.fromBytes(221, 240, 235, 160)
}
});
polygonCollection.push(redPolygon);
adding_billboard(-95.8079865631313, 30.24038650541154, "A", "-95.8079865631313, 30.24038650541154");
adding_billboard(-60.10509002138564, 23.526593580490083, "A", "-60.10509002138564, 23.526593580490083");
adding_billboard(-59.06372427570612, 2.245934026097194, "A", "-59.06372427570612, 2.245934026097194");
adding_billboard(-117.00668212362282, 3.938434130034481, "A", "-117.00668212362282, 3.938434130034481");
viewer.flyTo(redPolygon);
}
function adding_billboard(lon, lat, name, popup) {
var entity = viewer.entities.add({
name: name,
position: Cesium.Cartesian3.fromDegrees(lon, lat, 2000),
billboard: {
image: 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-512.png',
show: true, // default
pixelOffset: new Cesium.Cartesian2(0, -20), // default: (0, 0)
eyeOffset: new Cesium.Cartesian3(0.0, 0.0, 0.0), // default
horizontalOrigin: Cesium.HorizontalOrigin.bottom, // default
alignedAxis: Cesium.Cartesian3.ZERO, // default
width: 20, // default: undefined
height: 25, // default: undefined
//disableDepthTestDistance: Number.POSITIVE_INFINITY, // draws the label in front of terrain
// on ground show
},
label: {
text: popup,
font: "7pt sans-serif",
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BASELINE,
fillColor: Cesium.Color.BLACK,
showBackground: true,
backgroundColor: new Cesium.Color(1, 1, 1, 0.7),
backgroundPadding: new Cesium.Cartesian2(8, 4),
disableDepthTestDistance: Number
.POSITIVE_INFINITY, // draws the label in front of terrain
},
});
pointsCollection.push(entity);
}
var coordinates = [76.82071632075994, 33.4134542888633, 77.83750798568438, 33.39276536442791, 77.32892923803021,
32.93547457354476
];
var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function(click) {
var pickedObject = scene.pick(click.position);
if (Cesium.defined(pickedObject)) {
console.log("Second ");
console.log("pickedObject.id.id ", pickedObject.id.id);
console.log("pickedObject.id.name ", pickedObject.id.name);
console.log("pickedObject.id..polygon.hierarchy ", pickedObject.id.polygon.hierarchy.valueOf());
var data = pickedObject.id.polygon.hierarchy.valueOf();
console.log("data ", data.positions.valueOf());
pickedObject.id.polygon = {
hierarchy: new Cesium.CallbackProperty(new Cesium.Cartesian3.fromDegreesArray(coordinates),
false),
material: Cesium.Color.fromBytes(221, 240, 235, 160)
//pickedObject.id = redPolygon;// tried this but dailed due to same id then i removed it.
if (pickedObject.id.name == 'C') {
// $('#modal-activity').modal('show');
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
i want to shift polygon to some other coordiante but unable to use call back properly can some one guild me how can I do ?
i did some google searches which are given below but my issues not solved.
here i was trying to make polygon points dynamic but line disappers in tarrains. fist time it was ok after moving point it disappers.
https://sandcastle.cesium.com/?#c=zVTRbpswFP2VK14gEjMhWTaJ0Whd0odpnVpl0vZQ+uDCTWvN2Mg2yVjVf5/BkKRZ1VV9mnjhHp9zfH2uYUMVbBhuUcEJCNzCAjWrS/K9wwI/78qFFIYygcoffchE+2ysDoVhpomt0DmQDmCoCS2K4D4TAKxIwP+8jP2wrQQtESywwgLOOGeVRpACdK3WNEfHqaS2HlIkMLSyoMrYNyqmZK1kucRbhaiDN3E8I+MQpu/JeNRLmTAJdBvbiv1C/o39xgSm49BhueRSJTvjtiKrs2W/KmvD7SEXT5A+nZ8uvjym/WCFuUsgJrMWfsjEg8tmn8zkBclMXpPMP4N52wXz7v8Lpl1/LpVK8qbl7JvtD62Tw9t52aOXSlaoTHOqFG2CKyeBQ+YK16hQ5DhQg6O9w/5+whX4w2Y+XMMofLXb5Am3wex677t1Oc12QEkNKkb50VFdIhcu3K89Z9fA/aB+foQvHuLxGCdujHaQo8NxeqGXatNwnLvVj6yspDJQKx4QEhksK25b1dFNnf9EQ3KtW1kaDaK0YBv7EZxk3tEvJvMg51Rru7KueXdRM2+eRpb/SMYlLZi4vdig4rRpKXfx/NyBhJA0suXfKiMlv6HqwPEP
second :Dynamically change polygon position in cesium
it also gives error because i don't know where to use callback.
please guild me. thank you.
Here's Sandcastle link.
const {
Cartesian3,
CallbackProperty,
Color,
defined,
ScreenSpaceEventHandler,
ScreenSpaceEventType,
Viewer
} = window.Cesium;
const viewer = new Viewer("cesiumContainer");
let redPolygon;
const polygonCollection = [];
const pointsCollection = [];
const polygonId = "myArray";
let polygonPoints = [
-95.8079865631313, 30.24038650541154, -60.10509002138564, 23.526593580490083, -59.06372427570612,
2.245934026097194, -117.00668212362282, 3.938434130034481
];
function loadPoly() {
redPolygon = viewer.entities.add({
id: polygonId,
name: "myArray",
polygon: {
hierarchy: new CallbackProperty(() => {
return {
positions: Cartesian3.fromDegreesArray(polygonPoints)
};
}, false),
material: Color.fromBytes(221, 240, 235, 160)
}
});
polygonCollection.push(redPolygon);
adding_billboard(-95.8079865631313, 30.24038650541154, "A", "-95.8079865631313, 30.24038650541154");
adding_billboard(-60.10509002138564, 23.526593580490083, "A", "-60.10509002138564, 23.526593580490083");
adding_billboard(-59.06372427570612, 2.245934026097194, "A", "-59.06372427570612, 2.245934026097194");
adding_billboard(-117.00668212362282, 3.938434130034481, "A", "-117.00668212362282, 3.938434130034481");
viewer.flyTo(redPolygon);
}
function adding_billboard(lon, lat, name, popup) {
const entity = viewer.entities.add({
name: name,
position: Cartesian3.fromDegrees(lon, lat, 2000),
billboard: {
image: "https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-512.png",
show: true, // default
pixelOffset: new Cesium.Cartesian2(0, -20), // default: (0, 0)
eyeOffset: new Cartesian3(0.0, 0.0, 0.0), // default
horizontalOrigin: Cesium.HorizontalOrigin.bottom, // default
alignedAxis: Cesium.Cartesian3.ZERO, // default
width: 20, // default: undefined
height: 25 // default: undefined
//disableDepthTestDistance: Number.POSITIVE_INFINITY, // draws the label in front of terrain
// on ground show
},
label: {
text: popup,
font: "7pt sans-serif",
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BASELINE,
fillColor: Color.BLACK,
showBackground: true,
backgroundColor: new Color(1, 1, 1, 0.7),
backgroundPadding: new Cesium.Cartesian2(8, 4),
disableDepthTestDistance: Number.POSITIVE_INFINITY // draws the label in front of terrain
}
});
pointsCollection.push(entity);
}
loadPoly();
const scene = viewer.scene;
const handler = new ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (click) {
const pickedObject = scene.pick(click.position);
if (defined(pickedObject) && pickedObject.id && pickedObject.id.id === polygonId) {
const newPolygonPoints = [
76.82071632075994, 33.4134542888633, 77.83750798568438, 33.39276536442791, 77.32892923803021,
32.93547457354476
];
polygonPoints = newPolygonPoints;
viewer.camera.flyTo({
destination: Cartesian3.fromDegrees(76.82071632075994, 33.4134542888633, 1000)
});
}
}, ScreenSpaceEventType.LEFT_CLICK);

Javascript force insert string into JS object (JSON) leaflet vectortiles

I am using leaflet mapping library
for styling purposes this code works for the data_oh.parcel layer if I hardcode the layer name like this
var vectorTileOptions = {
interactive: true,
vectorTileLayerStyles: {
'data_oh.parcel': {
fillColor: "yellow",
fill: true,
color: "red"
}
}
};
However as I am going to be adding multiple layers I need to add that layer name as a variable so I have something like this
var layers={parcels: ["#Parcels","http://localhost:7800/data_oh.parcel/{z}/{x}/{y}.pbf",'#ffe4c4','data_oh.parcel'],
footpring: ["#Footprints","http://localhost:7800/data_oh.footprint/{z}/{x}/{y}.pbf",'#8b8878','data_oh.footprint']
};
var idArray = [];
var layerArray = [];
function legend_click(id, layer_api, color_layer,layer_name) {
$(id).click(function () {
var layer_add;
var i = idArray.indexOf(id);
if (i < 0) {
layer_add = L.vectorGrid.protobuf(layer_api, {
interactive: true,
rendererFactory: L.svg.tile,
vectorTileLayerStyles: {
layer_name: {
fillColor: "yellow",fill: true, color: "red"
}
}
})
layerArray.push(layer_add);
idArray.push(id);
}
else {
layer_add = layerArray[i];
};
if ($(id).prop('checked') == true) {
layer_add.addTo(map);
}
else if ($(id).prop('checked') == false) {
map.removeLayer(layer_add);
}
})
};
for (var key in layers){
legend_click(layers[key][0],layers[key][1],layers[key][2],layers[key][3])
};
In the legend_click function the 4th input is for that layer name but the color is not changing on the map, this means the 4th input is not being recognized correctly in the vectorTileLayerStyles portion of the code. Again if I hardcode the values it works but passing through on a variable holding a string it does not.
You can use computed property names inside legend_click:
vectorTileLayerStyles: {
[`${layer_name}`]: {
fillColor: "yellow",fill: true, color: "red"
}
}
so if that did not work this should work:
vectorTileLayerStyles: Object.fromEntries([[layer_name,{fillColor: "yellow", fill: true, color: "red"}]])

JSXGraph: Applying a line transformation with applyOnce()

I'm trying to apply a one-time transformation to a Line in JSXGraph.
Based on the documentation, I'm trying to transform a Line like this:
var f = function(x) {
return x;
};
var l1 = board.create('functiongraph', [f], {
name: 'line 1',
withLabel: true,
strokeWidth: 2,
strokeColor: 'orange',
fixed: false
});
// Rotate about an intersection point with another line
var i = board.create('intersection', [l1, l2, 0], {
name: 'intersection',
fixed: true,
showInfobox: false
});
var rot = board.create(
'transform', [
function() {
// This gets the value from a slider
return s.Value();
}, i
], {
type:'rotate'
});
rot.applyOnce(l1);
board.update();
The full source is here: http://maldive.ccnmtl.columbia.edu/js/ncustom.js and you can see the error I'm describing here: http://maldive.ccnmtl.columbia.edu/js/functiongraph-rotation.html
I get the error: t[n].coords is undefined. In the full application that I'm trying this in, I get the error: TypeError: Cannot read property 'usrCoords' of undefined
So, anyways, has anyone else tried to transform a line like this?
Update: After looking at the source, it's obvious this method only works with points. So, I don't know if this will be possible with my functiongraph. I am just using a straight line here though, so it's possible I can do something with points.
Yes, applyOnce is implemented only for points, texts and images, yet. The alternative is to bind the transformation to the curve. Here is the full example (relevant is only the very last line):
var board = JXG.JSXGraph.initBoard('jxgbox', {
boundingbox: [-10, 10, 10, -10],
axis:true
});
var f = function(x) {
return x;
};
var l1 = board.create('functiongraph', [f], {
name: 'line 1', withLabel: true, fixed: false
});
var f2 = function(x) {
var alpha = 0.3;
return (1 - alpha) *
(1.4 *
1.6 ** alpha) *
(x ** -alpha);
};
var l2 = board.create('functiongraph', [f2], {
name: 'line 2', withLabel: true, fixed: false
});
// Rotate about an intersection point with another line
var intrsct = board.create('intersection', [l1, l2, 0], {
name: 'intersection', fixed: true,
});
var s = board.create(
'slider', [
[-1, -1],
[2, -1],
[0, 0, 2]
], { name:'angle'});
var rot = board.create(
'transform', [
function() {
// This gets the value from a slider
return s.Value();
}, intrsct
], { type:'rotate' });
rot.bindTo(l1);
Alternatively, in the upcoming version 0.99.7 (already available in the nightly builds), you can create a new curve which is the transformed curve of an original curve:
var l1a = board.create('curve', [l1, rot], {
name: 'line 1a',
withLabel: true,
strokeWidth: 2,
strokeColor: 'orange',
fixed: false
});

Extremely Slow Initialization for Imagemapster Map

I am trying to initialize a map layout based on squares that refer to an ibeam location. There are over 500 json objects in some cases and it causes the initialization of the map to take for ever, sometimes between 30 and 60 seconds. Here is the javascript code I am using to initialize it, is there any way to speed up this initialization to be usable? It is an asp.net site if that helps any. Thanks in advance.
$(document).ready(function () {
$('img').mapster('resize', screen.width, screen.height, 0);
initialize();
});
function initialize() {
var ibeamid = '';
var status = '';
var redIbeams = [];
var yellowIbeams = [];
var greenIbeams = [];
$.each(json, function () {
status = this['status'];
ibeamid = this['ibeamName'];
if (status.toUpperCase() == 'G') {
greenIbeams.push(ibeamid);
}
else if (status.toUpperCase() == 'Y') {
yellowIbeams.push(ibeamid);
}
else if (status.toUpperCase() == 'R') {
redIbeams.push(ibeamid);
}
});
var basic_opts = {
mapKey: 'name'
};
var initial_opts = $.extend({}, basic_opts, {
fill: false,
stroke: true,
strokeWidth: 2,
strokeColor: '000000',
configTimeout: 30000,
render_select: {
fade: false,
//fill: true,
fillOpacity: 0.5
}
});
$('img').mapster(initial_opts)
.mapster('set', true, redIbeams, {
fill: true,
fillColor: 'ff0000',
staticState: true
//fillOpacity: 0.5
})
.mapster('set', true, yellowIbeams, {
fill: true,
fillColor: 'ffff00',
staticState: true
//fillOpacity: 0.5
})
.mapster('set', true, greenIbeams, {
fill: true,
fillColor: '00ff00',
staticState: true
//fillOpacity: 0.5
})
.mapster('snapshot').mapster('rebind', basic_opts);

How build array("key":value) in js with loop json ajax

I’m trying to modify a vector map with values obtained by AJAX and JSON.
Map function perfect with this array key value:
var visitorsData = {
"tac": 564,
"moq": 400,
"lim": 1000,
"apu": 800,
"caj": 760,
"ama": 300,
"lib": 700,
"lam": 600,
};
And the function that takes this values is this:
$('#world-map').vectorMap({
map: 'peru',
backgroundColor: '#fff',
regionStyle: {
initial: {
fill: "#c6c6c6",
stroke: "#204d6f",
"stroke-width": 1,
"stroke-opacity": 1
},
hover: {
fill: "#ed0000",
"fill-opacity": "1"
}
},
series: {
regions: [{
values: visitorsData,
scale: ["#3c8dbc", "#2D79A6"], //['#3E5E6B', '#A6BAC2'],
normalizeFunction: 'polynomial'
}]
},
onRegionLabelShow: function(e, el, code) {
if (typeof visitorsData[code] != "undefined")
el.html(el.html() + ': ' + visitorsData[code] + ' new visitors');
}
})
Now I want to take values taken from a JSON and with this build a new visitorsData but no function. This is my code:
$.ajax({
url: globalMapUrl, //obtain json
})
.done(function(data) {
var visitorsData = new Array();
for (var i = 0; i < data.length; i++) {
var item = data[i];
visitorsData[item.nombre] = item.numeroDeAnuncios;
}
})
.fail(function() {
alert("Ajax failed to fetch data")
})
Your visitorsData looks like a json object not a vector array
Create a new JSONObject instead of an Array and you should be good
instead of,
var visitorsData = new Array();
try
var visitorData = {};

Categories

Resources