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

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"}]])

Related

Code efficiency using VectorGrid in Leaflet

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)

How to pass variable in a variable in JavaScript?

I want to define a variable that contains a variable. In this example the variable that I want to pass is "filldata".
I have tried passing it using $filldata as well as +filldata+.
if (fill == true) {
$filldata = "fill: true,";
} else {
$filldata = "fill: false,";
};
if (amount == true) {
var set1 = {
label: "Earnings",
id: "earnings",
data: data1,
points: {
show: true,
},
bars: {
show: false,
barWidth: 12,
aling: 'center'
},
lines: {
show: true,
$filldata
},
yaxis: 1
};
} else {
var set1 = "";
}
Since you are just trying to create a boolean property named 'fill' with the value of some variable, also called fill (using fussy truthy/falsy values), then you can just skip creating the intermediate $filldata variable altogether and just create the property with the value evaluated inline. It's more succinct and more obvious.
Try:
if (amount == true) {
var set1 = {
label: "Earnings",
id: "earnings",
data: data1,
points: {
show: true,
},
bars: {
show: false,
barWidth: 12,
aling: 'center'
},
lines: {
show: true,
fill: fill==true
},
yaxis: 1
};
} else {
var set1 = "";
}
EDIT:
Also, note that it is not good practice to declare the variable set1 inside the if block scope if you intend to use it elsewhere. A better alternative would be:
var set1 = (amount == true) ?
{...your object as defined above...}
: "";

Extending Highchart's show() and hide() functions

What would be the best approach to extend the show() and hide() functions in Highcharts?
I want to add a boolean-like show(effectLinkedSeries) and hide(effectLinkedSeries), so I can control when a linkedSeries gets removed or added along with its "parent". Here is a demo of linkedSeries functionality. A property called "linkedTo" sets up the functionality:
series: [{
name: 'Temperature',
data: averages,
zIndex: 1,
marker: {
fillColor: 'white',
lineWidth: 2,
lineColor: Highcharts.getOptions().colors[0]
}
}, {
name: 'Range',
data: ranges,
type: 'arearange',
lineWidth: 0,
linkedTo: ':previous',
color: Highcharts.getOptions().colors[0],
fillOpacity: 0.3,
zIndex: 0
}]
I could modify the source directly but I'd rather try to extend the library, instead of hacking it.
Figured out I can make use of the Highcharts.Series.prototype to accomplish this
Highcharts.Series.prototype.setVisible = function (vis, redraw, effectLinkedSeries) {
var series = this,
chart = series.chart,
legendItem = series.legendItem,
showOrHide,
ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries,
oldVisibility = series.visible;
// if called without an argument, toggle visibility
series.visible = vis = series.userOptions.visible = vis === Highcharts.UNDEFINED ? !oldVisibility : vis;
showOrHide = vis ? 'show' : 'hide';
// show or hide elements
Highcharts.each(['group', 'dataLabelsGroup', 'markerGroup', 'tracker'], function (key) {
if (series[key]) {
series[key][showOrHide]();
}
});
// hide tooltip (#1361)
if (chart.hoverSeries === series || (chart.hoverPoint && chart.hoverPoint.series) === series) {
series.onMouseOut();
}
if (legendItem) {
chart.legend.colorizeItem(series, vis);
}
// rescale or adapt to resized chart
series.isDirty = true;
// in a stack, all other series are affected
if (series.options.stacking) {
Highcharts.each(chart.series, function (otherSeries) {
if (otherSeries.options.stacking && otherSeries.visible) {
otherSeries.isDirty = true;
}
});
}
// show or hide linked series
Highcharts.each(series.linkedSeries, function (otherSeries) {
if (effectLinkedSeries === true) {
otherSeries.setVisible(vis, false);
}
});
if (ignoreHiddenSeries) {
chart.isDirtyBox = true;
}
if (redraw !== false) {
chart.redraw();
}
Highcharts.fireEvent(series, showOrHide);
}; // end Highcharts.Series.prototype.setVisible
Highcharts.Series.prototype.show = function (effectLinkedSeries) {
this.setVisible(true,undefined,effectLinkedSeries);
};
Highcharts.Series.prototype.hide = function (effectLinkedSeries) {
this.setVisible(false,undefined,effectLinkedSeries);
};

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);

change color programmatically for FusionTablesLayer

I'm reading an array of polygons onto a Google Map using kml in a fusion table. I have an array of 4 colors, and I'd like to programmatically color the polygons one of those 4 colors, depending on the values in another array.
Somehow, the map only colors 4 polygons at a time, even when I specify that there are only 4 styles. How can I color all 130 polygons?
Here is my code:
function setInitialStyles() {
layer = new google.maps.FusionTablesLayer({
map : map,
query : {
select : "geometry",
from : "1gwSN6n_00uZ7YuAP7g4FiUiilybqDRlRmWJrpvA"
}
});
var options = {
styles : [
{
polygonOptions:
{
fillColor: "#ffffff",
strokeColor: "#bcbcbc",
fillOpacity: ".75"
}
}
]
};
var styles = [];
var style1 = candColor[0];
var style2 = candColor[1];
var style3 = candColor[2];
var style4 = candColor[3];
for (var i=0;i<countyCodes.length; i++) {
var c = countyCodes[i];
var whereClause = "'COUSUBFP' = " + c;
var myStyle;
if (countyColors[i] == "#0D58A6" ) { myStyle = style1; }
if (countyColors[i] == "#981400" ) { myStyle = style2; }
if (countyColors[i] == "#E3D132" ) { myStyle = style3; }
if (countyColors[i] == "#007F37" ) { myStyle = style4; }
options.styles.push({
where: whereClause,
polygonOptions: {
fillColor: myStyle
}
});
}
layer.setOptions(options);
}
You can't. Currently FusionTablesLayer is limited to one styled layer, which may have up to five applied styles. See the documentation about the limitation of FusionTablesLayer.
You can define general styling rules (like WHERE clauses) that are applied to all of your polygons. But again: you can only define 5 such rules.
layer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1gwSN6n_00uZ7YuAP7g4FiUiilybqDRlRmWJrpvA'
},
styles: [{
polygonOptions: {
fillColor: "#ffffff",
strokeColor: "#bcbcbc",
fillOpacity: ".75"
}
}, {
where: "population < 1000",
polygonOptions: {
fillColor: "#0000FF"
}
}, {
where: "population > 10000",
polygonOptions: {
fillOpacity: 1.0
}
}]
});
layer.setMap(map);
Your array of styles can only be 5 elements long as I mentioned in the last question you asked on this
This approach (using the Fusion Tables API v1, currently matching on name, not COUSUBFP, as your original table didn't include that column) might work for you, but it is rendering the polygons as native Google Maps API v3 objects, so there may be performance issues.

Categories

Resources