Openlayers feature not drawn without map refreshing - javascript

I am trying to draw the icon of the feature (defined as OpenLayers.Feature.Vector) after clicking on it (different than the original one, at the same point). The process of clicking works really good, however I want to change the graphic of clicked feature and I have problems with that.
What am I doing?
At first I declare both layers (one for the original, selectable feature and then for the one that should be drawn after the first one is selected) and add it to the map:
var firstLayer = new OpenLayers.Layer.Vector('Layer1');
map.addLayer(firstLayer);
var selectedLayer = new OpenLayers.Layer.Vector('Selected');
map.addLayer(selectedLayer);
The layers are clickable, proper handlers are attatched to them. What is more, I create styles for features that could be drawn on both layers:
// Styling feature of type1 for Layer1
type1Marker = openLayers.Util.applyDefaults(type1Marker, openLayers.Feature.Vector.style['default']);
type1Marker.externalGraphic = "somefile.svg";
type1Marker.pointRadius = iconsRadius;
type1Marker.fillOpacity = 1;
// Styling feature of type1 for Selected layer
type1MarkerSelected= openLayers.Util.applyDefaults(type1MarkerSelected, openLayers.Feature.Vector.style['default']);
eventObdConnectionLostIconSelectedStyle.externalGraphic ="somefile_selected.svg";
type1MarkerSelected.pointRadius = iconsRadius;
type1MarkerSelected.fillOpacity = 1;
// Styling feature of type2 for Layer1
type2Marker = openLayers.Util.applyDefaults(type2Marker, openLayers.Feature.Vector.style['default']);
type2Marker.externalGraphic = "somefile2.svg";
type2Marker.pointRadius = iconsRadius;
type2Marker.fillOpacity = 1;
// Styling feature of type2 for Selected layer
type2MarkerSelected= openLayers.Util.applyDefaults(type2MarkerSelected, openLayers.Feature.Vector.style['default']);
type2MarkerSelected.externalGraphic = "somefile2_selected.svg";
type2MarkerSelected.pointRadius = iconsRadius;
type2MarkerSelected.fillOpacity = 1;
Then some features for Layer1 are created at different points. I store those features in an array of features expanded by some additional data that I use later to distinguish the type of the marker that I want to use and I store the position of the marker on the map. And now there goes some logic that is executted after clicking on any feature :
var size = new OpenLayers.Size(30, 30);
var offset = new OpenLayers.Pixel(-(size.w / 2), -size.h);
var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
var marker = new OpenLayers.Feature.Vector(closestPoint, icon);
var markerSel = new OpenLayers.Feature.Vector(closestPoint.event.point, icon);
switch (closestPoint.type) {
case "type1":
markerSel.style = type1MarkerSelected;
break;
case "type2":
markerSel.style = type2MarkerSelected;
break;
}
var selectedLayer = map.getLayersByName('Selected')[0];
if (selectedLayer.features.length != 0)
selectedLayer.removeAllFeatures();
selectedLayer.addFeatures(markerSel);
selectedLayer.redraw({ force: true });
What is going on is really weird for me, i.e.: after clicking on the marker from Layer1 nothing happens, then if I scroll out the map it disappears and finally when I scroll more, the marker is displayed with the style I wanted (that is selected one). Especially the disappearing of the feature from Layer1 is something that I do not get at all - why it disappears while no operations on that layer are performed? I would like to stack the new icon above the one from Layer1 (zOrdering of the map is set to true and the Layer1 is defined before the Selected layer).
Does anybody know what could cause the problem? I have spent several hours on trying to fix it but now I'm out of ideas. Thank you in advance!

Related

Cesium JS - Accessing primitive attributes without using id

So essentially, I have a function that draws a colored rectangle at every globe coordinate point via coordinate and color arrays. (Frequency means new rectangle every x coordinates)
//Given an array of coordinates, respective colors, and level of detail,
//Draws heatmap on the globe
function DrawMapGivenArrays(CoordinateArray, Colors, frequency)
{
var instances = [];
for(var i = 0; i < CoordinateArray.length; i++)
{
var Cartesian1 = new Cesium.Cartesian3.fromDegrees(CoordinateArray[i].lon,
CoordinateArray[i].lat);
var Cartesian2 = new Cesium.Cartesian3.fromDegrees(CoordinateArray[i].lon+frequency,
CoordinateArray[i].lat-frequency);
var CartesianArray = Array();
CartesianArray.push(Cartesian1);
CartesianArray.push(Cartesian2);
var newPrim = new Cesium.GeometryInstance
({
geometry : new Cesium.RectangleGeometry
({
rectangle : Cesium.Rectangle.fromCartesianArray(CartesianArray),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes :
{
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Colors[i])
},
id: "Rectangle" + i,
});
instances.push(newPrim);
numberOfRectangles++;
}
var primitive = new Cesium.Primitive
({
releaseGeometryInstances : false,
geometryInstances : instances,
appearance : new Cesium.PerInstanceColorAppearance(),
});
scene.primitives._primitives[1] = primitive;
}
That works fine.. After I draw the map, I'm using this small function below to individually edit the color of one rectangle. (I call this for every rectangle to change all of them).
//Changes the color of a rectangle primitive given its unique id and a color value
function setPrimitiveRectangle(id, color)
{
var CesiumColor = Cesium.ColorGeometryInstanceAttribute.toValue(color);
scene.primitives._primitives[1].getGeometryInstanceAttributes(id).color = CesiumColor; //this line is 10x slower for every instance after it runs the 1st time
//".getGeometryInstanceAttributes(id)" This specific phrase runs 10x slower after 1st instance
}
That works as well. But, for some reason, it has issues.
For example, when I re-color all of the rectangles the first time, it runs very fast. However, every time I re-fun that function again after the first time, it's 10x slower. I narrowed it down to the phrase that was giving me problems( ".getGeometryInstanceAttributes(id)" ).
I tried to circumvent calling the get function by modifying color values directly with this: (Where i is iterating over every rectangle).
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[0] = 0;
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[1] = 0;
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[2] = 0;
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[3] = 0;
Once I do this, I can check in chrome and see that the values located at those areas changed, however, the colors of the rectangles do not update.
I don't understand why ".getGeometryInstanceAttributes(id)" runs 10x slower after it's called the first time, and why I cannot directly modify viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[0].
Thanks

AmCharts remove gap at start of serialChart

I am trying AmCharts for the first time and having trouble with one small thing. I created a graph using the following javascript:
dayGraph = new AmCharts.AmGraph();
dayGraph.valueField = "value";
dayGraph.type = "line";
dayGraph.balloonText = "<b>[[value]]</b>";
dayGraph.connect = false;
dayGraph.lineThickness = 2;
dayGraph.lineColor = "#8B0000";
dayGraph.fillColor = "#8B0000";
dayGraph.fillAlphas = 0.5;
chartCursor = new AmCharts.ChartCursor();
energyChart = new AmCharts.AmSerialChart();
energyChart.categoryField = "time";
/* energyChart.startDuration = 1;*/
energyChart.addGraph(dayGraph);
energyChart.categoryAxis.parseDates = true;
energyChart.categoryAxis.minPeriod = "mm";
energyChart.chartCursor = chartCursor;
energyChart.categoryAxis.equalSpacing = true;
It seems to work well, but I can't seem to find a way to remove the gap at the start of the graph (before the 00:00 value). The first data point is exactly at 00:00, so I would expect this point to sit on the vertical axis. Instead, there is a small gap. See the images below for how it currently is, and how I want it to look.
The current graph looks like this:
I want it to look like this:
Each serial chart already has categoryAxis property with a reference to CategoryAxis object. So you can just set it's startOnAxis property to true:
energyChart.categoryAxis.startOnAxis = true;
or, if you need to instantiate your own:
energyChart.categoryAxis = new AmCharts.CategoryAxis();
energyChart.categoryAxis.startOnAxis = true;
Whatever floats your boat.
Also, it's worth noting, that for date-based category axes, startOnAxis will work only if equalSpacing is set to true.

How to change cluster zoom level on click with Leaflet map?

I have a leaflet map that has zoom levels 2-7 and uses the MarkerCluster plugin, by default I have the L.MarkerClusterGroup disable clustering a zoom level 2 (which means no clustering) and I'm trying to allow the user to click a button that then changes the clustering zoom level to 5. Is this possible?
I know I could do it by making two markercluster groups, one that has no clustering and one that has clustering and remove/add it based on click but that just seems incredibly messy. Really, there's several ways to do it but they are so incredibly clunky.
Code:
Default (2 is the lowest level of zoom):
var markers = new L.MarkerClusterGroup (
{
disableClusteringAtZoom: 2,
maxClusterRadius: 100,
animateAddingMarkers: true
});
What I want to do be able to do:
$('#mcluster').click(function() {
//do some code that sets the disableClusterAtZoom to 5
});
I could not find a way to disable clustering or set a new value for disableClustering at zoom, but I found a less clunky way of achieving this.
var markers = new L.LayerGroup(); //non cluster layer is added to map
markers.addTo(map);
var clusters = new L.MarkerClusterGroup (
{
disableClusteringAtZoom: 5,
maxClusterRadius: 100,
animateAddingMarkers: true
}); //cluster layer is set and waiting to be used
var clusterStatus = 'no'; //since non cluster group is on by default, the status for cluster is set to no
$('#mcluster').click(function( event ) {
if(clusterStatus === 'no'){
clusterStatus = 'yes';
var current1 = markers.getLayers(); //get current layers in markers
map.removeLayer(markers); // remove markers from map
clusters.clearLayers(); // clear any layers in clusters just in case
current1.forEach(function(item) { //loop through the current layers and add them to clusters
clusters.addLayer(item);
});
map.addLayer(clusters);
} else {
clusterStatus = 'no'; //we're turning off clustering here
var current2 = clusters.getLayers(); //same code as before just reversed
map.removeLayer(clusters);
markers.clearLayers();
current2.forEach(function(item) {
markers.addLayer(item);
});
map.addLayer(markers);
}
});
I'm sure there is a more elegant solution but with my still growing knowledge this is what I came up with.
I know you needed a solution a few months ago, but just to let you know that I released recently a sub-plugin for Leaflet.markercluster that can perform exactly what you are looking for (with a few extra code): Leaflet.MarkerCluster.Freezable (demo here).
var mcg = L.markerClusterGroup().addTo(map),
disableClusteringAtZoom = 2;
function changeClustering() {
if (map.getZoom() >= disableClusteringAtZoom) {
mcg.disableClustering(); // New method from sub-plugin.
} else {
mcg.enableClustering(); // New method from sub-plugin.
}
}
map.on("zoomend", changeClustering);
$('#mcluster').click(function () {
disableClusteringAtZoom = (disableClusteringAtZoom === 2) ? 5 : 2;
changeClustering();
});
mcg.addLayers(arrayOfMarkers);
// Initially disabled, as if disableClusteringAtZoom option were at 2.
changeClustering();
Demo: http://jsfiddle.net/fqnbwg3q/3/
Note: in the above demo I used a refinement to make sure the markers merge with animation when clustering is re-enabled. Simply use a timeout before using enableClustering():
// Use a timeout to trigger clustering after the zoom has ended,
// and make sure markers animate.
setTimeout(function () {
mcg.enableClustering();
}, 0);

OpenLayers - Mosaic of custom images on one layer

Is it possible to have a layer made up of a mosaic of custom images?
I've been only able to get a single custom image on a given layer via OpenLayers.Layer.Image. Essentially, if I could find a way to specify custom images for the tiles of a given layer, then my problem would be solved.
I have tried various combinations of OpenLayers.Tile, OpenLayers.Tile.Image, OpenLayers.Layer and OpenLayers.Layer.Grid but haven't been able to things working.
The basic flow I follow is:
var map = new OpenLayers.Map('map');
var layer = new <OpenLayers.Layer | OpenLayers.Layer.Grid> (<parameters>);
var tile1 = new <OpenLayers.Tile | OpenLayers.Tile.Image> (<parameters>);
map.addLayer(layer);
map.zoomToMaxExtent();
Specific examples of how I initialize each constructor are provided below.
Regarding OpenLayers.Layer.Grid, I'm actually not sure what to specify for the url and params constructor parameters.
Any advice on whether this works and/or if I'm on the right track would be greatly appreciated.
OpenLayers.Layer
var layer = new OpenLayers.Layer(
'layer_name',
{
isBaseLayer: true
}
);
OpenLayers.Layer.Grid
var layer = new OpenLayers.Layer.Grid(
'layer_name',
?url?,
?params?
);
OpenLayers.Tile
var layer = new OpenLayers.Tile(
layer_name,
new OpenLayers.Pixel(0,0),
new OpenLayers.Bounds(-1,-1,1,1),
'square1.jpg',
new OpenLayers.Size(300,300)
);
OpenLayers.Tile.Image
var layer = new OpenLayers.Tile.Image(
layer_name,
new OpenLayers.Pixel(0,0),
new OpenLayers.Bounds(-1,-1,1,1),
new OpenLayers.Size(300,300),
{
url: 'square1.jpg'
}
);
Have you tried Zoomify layer? Here's the example. It allows you to load in the map all images from a given directory, named in the form {z}-{x}-{y}.jpg, where {z} is the zoom level.
If you need to break up an image into smaller tiles, I suggest using this free MapTiler software, which will create the tiles for as many zoom levels as you need.
You can create a map layer of image tiles using a TMS Layer:
var layer = new OpenLayers.Layer.TMS("TMS Layer","",
{url: '', serviceVersion: '.', layername: '.', alpha: true,
type: 'png', getURL: getTileURL
}
);
map.addLayer(layer);
The getTileURL function is used by the TMS layer to find the tiled images to display. This function assumes that the images are stored in a hierarchical structure like the one created by MapTiler.
Ex: img/tiles/7/4/1.png is the image that is 5th from the left and 2nd from the bottom of zoom level 7.
function getTileURL(bounds)
{
var res = this.map.getResolution();
var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
var y = Math.round((bounds.bottom - this.maxExtent.bottom) / (res * this.tileSize.h));
var z = this.map.getZoom();
var path = "img/tiles/" + z + "/" + x + "/" + y + "." + this.type;
var url = this.url;
if (url instanceof Array)
{
url = this.selectUrl(path, url);
}
return url + path;
}

Openlayers - arrary of info into popupmarker

I have a connection to a database(db). I am getting the lon, lat and name from the db and stroing them:
while ($row_ChartRS = mysql_fetch_array($sql1))
{
$latitude=$row_ChartRS['latitude'];
$longitude=$row_ChartRS['longitude'];
$bus_name =$row_ChartRS['short_name'];
//echo $latitude.'--'.$longitude.'<br>';
echo $bus_name;
I then create a map to display the data. The markers are working fine for all lat, lon locations. Code:
function init()
{
projLonLat = new OpenLayers.Projection("EPSG:4326"); // WGS 1984
projMercator = new OpenLayers.Projection("EPSG:900913"); // Spherical Mercator
overviewMap = new OpenLayers.Control.OverviewMap();
//adding scale ruler
scale = new OpenLayers.Control.ScaleLine();
scale.geodesic = true; // get the scale projection right, at least on small
map = new OpenLayers.Map('demoMap',
{ controls: [ new OpenLayers.Control.Navigation(), // direct panning via mouse drag
new OpenLayers.Control.Attribution(), // attribution text
new OpenLayers.Control.MousePosition(), // where am i?
new OpenLayers.Control.LayerSwitcher(), // switch between layers
new OpenLayers.Control.PanZoomBar(), // larger navigation control
scale,
overviewMap // overview map
]
}
);
map.addLayer(new OpenLayers.Layer.OSM.Mapnik("Mapnik"));
map.addLayer(new OpenLayers.Layer.OSM.Osmarender("Osmarender"));
//Create an explicit OverviewMap object and maximize its size after adding it to the map so that it shows
//as activated by default.
overviewMap.maximizeControl();
//Adding a marker
markers = new OpenLayers.Layer.Markers("Vehicles");
map.addLayer(markers);
vectorLayer = new OpenLayers.Layer.Vector('Routes');
map.addLayer(vectorLayer);
for (k in Locations)
{
//adding a popup for the marker
var feature = new OpenLayers.Feature(markers, new OpenLayers.LonLat(Locations[k].lon, Locations[k].lat).transform(projLonLat,projMercator));
//true to close the box
feature.closeBox = true;
feature.popupClass = new OpenLayers.Class(OpenLayers.Popup.AnchoredBubble,
{
//create the size of the box
'autoSize': true,
'maxSize': new OpenLayers.Size(100,100)
});
//add info into box
for (z in names)
{
feature.data.popup = new OpenLayers.Feature(new OpenLayers.LonLat(names[z]).transform(projLonLat,projMercator));
}
//puts a scroll button on box to scroll down to txt
//feature.data.overflow = "auto";
marker = feature.createMarker();
marker.display(true);
markerClick = function (evt) {
if (this.popup == null) {
this.popup = this.createPopup(this.closeBox);
map.addPopup(this.popup);
this.popup.show();
} else {
this.popup.toggle();
}
currentPopup = this.popup;
OpenLayers.Event.stop(evt);
};
marker.events.register("mousedown", feature, markerClick);
markers.addMarker(marker);
map.setCenter(new OpenLayers.LonLat(Locations[k].lon, Locations[k].lat).transform(projLonLat,projMercator), zoom);
var lonLat1 = new OpenLayers.LonLat(Locations[k].lon,Locations[k].lat).transform(new OpenLayers.Projection('EPSG:4326'), map.getProjectionObject());
var pos2=new OpenLayers.Geometry.Point(lonLat1.lon,lonLat1.lat);
points1.push(pos2);
//Uncomment to put boxes in when map opens
//feature.popup = feature.createPopup(feature.closeBox);
//map.addPopup(feature.popup);
//feature.popup.show()
}
var lineString = new OpenLayers.Geometry.LineString(points1);
var lineFeature = new OpenLayers.Feature.Vector(lineString,'',style_green);
vectorLayer.addFeatures([lineFeature]);
map.setCenter(lonLat1,zoom);
} //function
However the name in the popup marker is the same for all markers. i.e. the last name pulled from the db. Can anyone please help with this - I have spent 3 full days trying to fix it!
Thanks in advance!
A few comments:
The PHP code you’ve posted is completely irrelevant, since it’s not seen to be used anywhere.
The objects names and Locations aren’t declared anywhere in the code you posted. What do they contain?
In the code quoted below, you’re creating multiple new Feature objects, but you assign them all to the same property (thereby overwriting that property each time). Is that intentional?
//add info into box
for (z in names) {
feature.data.popup = new OpenLayers.Feature(new OpenLayers.LonLat(names[z]).transform(projLonLat,projMercator));
}
Edit:
This does appear to be where it’s going wrong. You should remove the for...z loop, and replace it with the following code:
//add info into box
feature.data.popup = new OpenLayers.Feature(new OpenLayers.LonLat(names[k]).transform(projLonLat,projMercator));
Since in PHP, you’re using the same index ($v) to fill both arrays, it makes sense to use the same index to read them in javascript...
Aside from that, using the for...in loop on Javascript arrays is not considered good practice, for a number of reasons. It’s better to use the following:
for (k = 0; k < Locations.length; k += 1) {
// your code
}
i had the same problem , and i solve it ...
the problem is overwrite
you don't have to loop inside your function , do the loop for function for example:
function init(z)
{
feature.data.popup = new OpenLayers.Feature(new OpenLayers.LonLat(names[z]).transform(projLonLat,projMercator));
}
for (z in names)
{
init(z)
}

Categories

Resources