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);
Related
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"}]])
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)
Hi I am using google maps api(JavaScript) to build an interactive world map. It went really well until I ran into this problem. I am using polygons to show to outline of a country. These polygons trigger a modal showing information about the country when clicked on. This worked until I started to use "Data Layer: Earthquake data". Instead of using earthquake data I use sales information of the company I work at. So if a large share of our customers are from the Netherlands then the datalayer assigned to the Netherlands will be very large. The problem is that because of the datalayers the countries are no longer clickable. I can not click "through" the datalayer. Is there a possibility that I can trigger the event behind the datalayer?
This code displays the datalayers:
map.data.loadGeoJson('./data/test.json');
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0
}
})
});
map.data.addListener('mouseover', function(event) {
map.data.overrideStyle(event.feature, {
title: 'Hello, World!'
});
});
map.data.addListener('mouseout', function(event) {
map.data.revertStyle();
});
function eqfeed_callback(data) {
map.data.addGeoJson(data);
}
This code displays the polygons:
function drawMap(data) {
var rows = data['rows'];
for (var i in rows) {
if (rows[i][0] != 'Antarctica') {
var newCoordinates = [];
var geometries = rows[i][1]['geometries'];
if (geometries) {
for (var j in geometries) {
newCoordinates.push(constructNewCoordinates(geometries[j]));
}
} else {
newCoordinates = constructNewCoordinates(rows[i][1]['geometry']);
}
var country = new google.maps.Polygon({
paths: newCoordinates,
strokeColor: 'transparent',
strokeOpacity: 1,
strokeWeight: 0.3,
fillColor: '#cd0000',
fillOpacity: 0,
name: rows[i][0]
});
google.maps.event.addListener(country, 'mouseover', function() {
this.setOptions({
fillOpacity: 0.3
});
});
google.maps.event.addListener(country, 'mouseout', function() {
this.setOptions({
fillOpacity: 0
});
});
google.maps.event.addListener(country, 'click', function() {
var countryName = this.name;
var code = convert(countryName); // Calls a function that converts the name of the country to its official ISO 3166-1 alpha-2 code.
var modal = document.querySelector('.modal');
var instance = M.Modal.init(modal);
instance.open();
});
country.setMap(map);
}
}
If read in the documentation that changing the zIndex won't work because "Markers are always displayed in front of line-strings and polygons."
Is there a way to click on a polygon behind a datalayer?
EDIT
I tried to give the polygon a higher zIndex and I made the datalayer not clickable
map.data.loadGeoJson('./data/test.json');
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0,
clickAble: false,
zIndex: 50
}
})
});
function eqfeed_callback(data) {
map.data.addGeoJson(data);
}
function drawMap(data) {
var rows = data['rows'];
for (var i in rows) {
if (rows[i][0] != 'Antarctica') {
var newCoordinates = [];
var geometries = rows[i][1]['geometries'];
if (geometries) {
for (var j in geometries) {
newCoordinates.push(constructNewCoordinates(geometries[j]));
}
} else {
newCoordinates = constructNewCoordinates(rows[i][1]['geometry']);
}
var country = new google.maps.Polygon({
paths: newCoordinates,
strokeColor: 'transparent',
strokeOpacity: 1,
strokeWeight: 0.3,
fillColor: '#cd0000',
fillOpacity: 0,
name: rows[i][0],
zIndex: 100
});
google.maps.event.addListener(country, 'mouseover', function() {
this.setOptions({
fillOpacity: 0.3
});
});
google.maps.event.addListener(country, 'mouseout', function() {
this.setOptions({
fillOpacity: 0
});
});
google.maps.event.addListener(country, 'click', function() {
var countryName = this.name;
var code = convert(countryName); // Calls a function that converts the name of the country to its official ISO 3166-1 alpha-2 code.
var modal = document.querySelector('.modal');
var instance = M.Modal.init(modal);
instance.open();
});
country.setMap(map);
}
}
//console.log(map);
//test(map)
}
EDIT
Apparently the datalayer wasn't the problem, but the icon was. That is why it didn't work when I did this:
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0,
clickable: false
}
})
});
The correct way to do it is this:
map.data.setStyle(function(feature) {
var percentage = parseFloat(feature.getProperty('percentage'));
return ({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: percentage,
fillColor: '#00ff00',
fillOpacity: 0.35,
strokeWeight: 0
},
clickable: false
})
});
You basically have 2 options here:
Set the zIndex of your Polygons to a higher number than the data layer. Your Polygons will be clickable but obviously will appear above the data layer, which might not be what you want.
Set the clickable property of the data layer to false so that you can click elements that are below. This will work if you don't need to react to clicks on the data layer...
Option 2 example code:
map.data.setStyle({
clickable: false
});
Edit: Full working example below, using option 2. As you can see the Polygon is below the data layer but you can still click it.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: {
lat: -28,
lng: 137
}
});
var polygon = new google.maps.Polygon({
strokeOpacity: 0,
strokeWeight: 0,
fillColor: '#00FF00',
fillOpacity: .6,
paths: [
new google.maps.LatLng(-26, 139),
new google.maps.LatLng(-23, 130),
new google.maps.LatLng(-35, 130),
new google.maps.LatLng(-26, 139)
],
map: map
});
polygon.addListener('click', function() {
console.log('clicked on polygon');
});
// Load GeoJSON
map.data.loadGeoJson('https://storage.googleapis.com/mapsdevsite/json/google.json');
// Set style
map.data.setStyle({
fillColor: '#fff',
fillOpacity: 1,
clickable: false
});
}
#map {
height: 200px;
}
<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
<div id="map"></div>
I have found that after, setting the z-order, the maps api does not reliably send clicks to polygon feature in the top layer when there are many polygons.
I had one data layer of regions where each feature is a precinct boundary. When you click on one feature, it loads another data layer on top. The top layer consists of polygons inside the region with a higher z-order, representing house title boundaries within that region.
After the houses are loaded, clicking on a house should send the click to the house polygon, not the region. But this sometimes failed - especially if there are many houses.
To resolve the issue, after clicking on a region feature, I set that feature to be non clickable. Then the clicks always propagate to the correct house feature. You can still click on other features of the lower layer, just not the selected one. This solution should work if your data and presentation follows a similar pattern.
/* private utility is only called by this.hideOnlyMatchingFeaturesFromLayer() */
_overrideStyleOnFeature(feature, layer, key, value, overrideStyle, defaultStyle) {
if (feature.getProperty(key) === value) {
if (this.map) {
layer.overrideStyle(feature, overrideStyle);
}
} else {
if (this.map) {
layer.overrideStyle(feature, defaultStyle);
}
}
}
/* Apply an overrideStyle style to features in a data layer that match key==value
* All non-matching features will have the default style applied.
* Otherwise all features except the matching feature is hidden!
* Examples:
* overrideStyle = { clickable: false,strokeWeight: 3}
* defaultStyle = { clickable: true,strokeWeight: 1}
*/
overrideStyleOnMatchingFeaturesInLayer(layer, key, value, overrideStyle, defaultStyle) {
layer.forEach((feature) => {
if (Array.isArray(feature)) {
feature.forEach((f) => {
_overrideStyleOnFeature(f, layer, key, value, overrideStyle, defaultStyle);
});
} else {
_overrideStyleOnFeature(feature, layer, key, value, overrideStyle, defaultStyle);
}
});
}
/* example usage */
overrideStyleOnMatchingFeaturesInLayer(
theRegionsDataLayer,
'PROP_NAME',
propValue,
{ clickable: false, strokeWeight: 3},
{ clickable: true, strokeWeight: 1}
);
I am displaying jqplot meter gauge inside modal window and I want to update it after every 5 seconds. I wrote the following code but it is not working
$(document).ready(function(){
s1 = [Math.floor(Math.random()*(401)+100)];
plot3 = $.jqplot('meter',[s1],{
seriesDefaults: {
renderer: $.jqplot.MeterGaugeRenderer,
rendererOptions: {
min: 100,
max: 500,
intervals:[200, 300, 400, 500],
intervalColors:['#66cc66', '#93b75f', '#E7E658', '#cc6666'],
smooth: true,
animation: {
show: true
}
}
}
});
$('a[href=\"#yw1_tab_3\"]').on('shown', function(e) {
if (plot3._drawCount === 0) {
plot3.replot();
}
});
windows.setInterval(function() { plot3.destroy();
s1 = [Math.floor(Math.random()*(401)+100)];
plot3.replot();
}, 5000);
});
How can I update meter gauge every 5 seconds without updating whole page?
here is the fix: JsFiddle link
$(document).ready(function () {
var s1 = [Math.floor(Math.random() * (401) + 100)];
var plot3 = $.jqplot('meter', [s1], {
seriesDefaults: {
renderer: $.jqplot.MeterGaugeRenderer,
rendererOptions: {
min: 100,
max: 500,
intervals: [200, 300, 400, 500],
intervalColors: ['#66cc66', '#93b75f', '#E7E658', '#cc6666'],
smooth: true,
animation: {
show: true
}
}
}
});
setInterval(function () {
s1 = [Math.floor(Math.random() * (401) + 100)];
plot3.series[0].data[0] = [1,s1]; //here is the fix to your code
plot3.replot();
}, 1000);
});
I have a KML file with more than 10 placemarks, using OpenLayers. What I want to do is, when I click on radiobutton, a specific placemark change its color.
Does anybody know how to do that?
Thanks.
EDITED:
So here is what I have so far:
function init(){
///////////////////////////////////////////////
CONTROLS AND MAP STUFF
//////////////////////////////////////////////
var myvector = new OpenLayers.Layer.Vector("myvector", {
projection: map.displayProjection,
styleMap: new OpenLayers.StyleMap(
{ 'default':
{
strokeColor: "#777777",
strokeOpacity: 1,
strokeWidth: "2",
fillColor: "#FFF900",
fillOpacity: 1,
pointRadius: 8,
pointerEvents: "visiblePainted",
graphicName: "circle",
}
}),
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: url_time,
format: new OpenLayers.Format.KML({
extractStyles: false,
extractAttributes: true
})
})
});
map.addLayers([wms, wms2, myvector]);
select = new OpenLayers.Control.SelectFeature(myvector);
myvector.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
map.addControl(select);
select.activate();
map.zoomToExtent(new OpenLayers.Bounds(-53,-21,13,22));
}
function switchLabels() {
/////// PROBABLY HERE IS THE PLACE TO DO THE TRICK ////////
myvector.redraw();
}
///////////////////////////////////////////////
SOME OTHER THINGS
//////////////////////////////////////////////
And the radion button:
<input name="button1" type="radio" value="button1" onClick="switchLabels()">
Here is a post showing this switchLabels, but I have no idea how to change a point created by one placemark.
Thanks.
OK. I gave up using KML to do what I wanted. For those who need to change some attributes of vectors in OpenLayers, here goes a possible solution:
var features = [];
features[0] = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(10,10),
{
name : "Hello",
body : "world",
}, {strokeColor: "#777777",strokeOpacity: 1,strokeWidth: "2",fillColor: "#FFF900",fillOpacity: 1,pointRadius: 8,pointerEvents: "visiblePainted",graphicName: "circle"});
features[1] = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(15,10),
{
name : "Hello",
body : "Mars",
}, {strokeColor: "#777777",strokeOpacity: 1,strokeWidth: "2",fillColor: "#FFF900",fillOpacity: 1,pointRadius: 8,pointerEvents: "visiblePainted",graphicName: "circle"});
///////////// POPUPS //////////////
vector = new OpenLayers.Layer.Vector("LAYER",{
eventListeners:{
'featureselected':function(evt){
var feature = evt.feature;
var popup = new OpenLayers.Popup.FramedCloud("popup",
OpenLayers.LonLat.fromString(feature.geometry.toShortString()),
new OpenLayers.Size(350,350),
feature.attributes.name + feature.attributes.body,
null, false, onPopupClose
);
popup.maxSize = new OpenLayers.Size(350, 350);
popup.minSize = new OpenLayers.Size(350, 350);
feature.popup = popup;
map.addPopup(popup);
},
'featureunselected':function(evt){
var feature = evt.feature;
map.removePopup(feature.popup);
feature.popup.destroy();
feature.popup = null;
}
}
});
function onPopupClose(evt) {
select.unselectAll();
}
vector.addFeatures(features);
// create the select feature control
var selector = new OpenLayers.Control.SelectFeature(vector,{
click:true,
autoActivate:true
});
map.addLayers([vector]);
map.addControl(selector);
map.zoomToExtent(new OpenLayers.Bounds(-53,-21,13,22));
}
///////////////////// FUNCTION TO CHANGE THE VECTOR STYLE ///////////
function switchColors(p_ind,p_id) {
if (eval('document.form.' + p_id).checked == 1){
vector.features[p_ind].style = {
strokeColor: "#777777",
strokeOpacity: 1,
strokeWidth: "2",
fillColor: "#FF0000",
fillOpacity: 1,
pointRadius: 8,
pointerEvents: "visiblePainted",
graphicName: "circle"
};
}
else {
vector.features[p_ind].style = {
strokeColor: "#777777",
strokeOpacity: 1,
strokeWidth: "2",
fillColor: "#FFF900",
fillOpacity: 1,
pointRadius: 8,
pointerEvents: "visiblePainted",
graphicName: "circle"
};
}
vector.redraw();
}
And then the radio:
<form name="form" method="post" action="action.php">
<input name="p1" type="checkbox" value="p1" onClick="switchColors(0,'p1');">
<input name="p2" type="checkbox" value="p2" onClick="switchColors(1,'p2');">
</form>