ol3 ext-ol how can make cluster for different layers - openlayers-3

I'm using ol3/ol4 with ol-ext
I create two layer:
clusterSource = new ol.source.Cluster({
distance: distanceFt,
source: new ol.source.Vector()
});
// Animated cluster layer
clusterLayer = new ol.layer.AnimatedCluster({
name: 'Cluster',
source: clusterSource,
animationDuration: 700, //$("#animatecluster").prop('checked') ? 700 : 0,
// Cluster style
style: getStyle
});
layersArray.push(clusterLayer); // adding to array
sourceReclamos_Eventos = new ol.source.Cluster({
distance: distanceFt,
source: new ol.source.Vector()
});
capaReclamos_Eventos = new ol.layer.AnimatedCluster({
name: "Reclamos_Eventos",
source: sourceReclamos_Eventos,
animationDuration: 700,
style: getStyle
});
layersArray.push(capaReclamos_Eventos);
Later, add that layers in:
selectCluster = new ol.interaction.SelectCluster({
layers: arraySelectCLuster,
// Point radius: to calculate distance between the features
pointRadius: 20,
animate: true, //$("#animatesel").prop('checked'),
// Feature style when it springs apart
featureStyle: featureStyle,
selectCluster: false, // disable cluster selection
});
After load the layers, only persist the Features in the first layer, in the second layer the Features is removed (clear) after zoom changing... why?
please, help
EDIT
I'm adding features using clusterLayer.getSource().addFeatures() and capaReclamos_Eventos.getSource().addFeatures().
function addFeatures_Reclamos_Eventos(ffs, centrar) {
var transform = ol.proj.getTransform('EPSG:4326', 'EPSG:3857');
var features = [];
for (var i = 0; i < ffs.length; i++) {
features[i] = new ol.Feature();
features[i].setProperties(ffs[i]);
var geometry = new ol.geom.Point(transform([parseFloat(ffs[i].lon), parseFloat(ffs[i].lat)]));
features[i].setGeometry(geometry);
}
qweFeature = features;
capaReclamos_Eventos.getSource().addFeatures(features);
removeloading('mapLoading');
if (document.getElementById('botonFiltrar')) {
document.getElementById('botonFiltrar').disabled = false;
}
if (centrar) {
window.setTimeout(function () {
var extent = capaReclamos_Eventos.getSource().getExtent();
map.getView().fit(extent, map.getSize());
}, 700);// 1/2 seg
}
}

Related

Drag and Drop limitation in Konva js

I recently began to learn Konva-JS... please help me :)
<script>
var width = window.innerWidth;
var height = window.innerHeight;
function loadImages(sources, callback) {
var assetDir = '/assets/';
var images = {};
var loadedImages = 0;
var numImages = 0;
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = assetDir + sources[src];
}
}
function isNearOutline(animal, outline) {
var a = animal;
var o = outline;
var ax = a.getX();
var ay = a.getY();
if(ax > o.x - 20 && ax < o.x + 20 && ay > o.y - 20 && ay < o.y + 20) {
return true;
}
else {
return false;
}
}
function drawBackground(background, beachImg, text) {
var context = background.getContext();
context.drawImage(beachImg, 0, 0);
context.setAttr('font', '20pt Calibri');
context.setAttr('textAlign', 'center');
context.setAttr('fillStyle', 'white');
context.fillText(text, background.getStage().getWidth() / 2, 40);
}
function initStage(images) {
var stage = new Konva.Stage({
container: 'container',
width: 578,
height: 530
});
var background = new Konva.Layer();
var animalLayer = new Konva.Layer();
var animalShapes = [];
var score = 0;
// image positions
var animals = {
snake: {
x: 10,
y: 70
},
giraffe: {
x: 90,
y: 70
},
monkey: {
x: 275,
y: 70
},
lion: {
x: 400,
y: 70
}
};
var outlines = {
snake_black: {
x: 275,
y: 350
},
giraffe_black: {
x: 390,
y: 250
},
monkey_black: {
x: 300,
y: 420
},
lion_black: {
x: 100,
y: 390
}
};
// create draggable animals
for(var key in animals) {
// anonymous function to induce scope
(function() {
var privKey = key;
var anim = animals[key];
var animal = new Konva.Image({
image: images[key],
x: anim.x,
y: anim.y,
draggable: true
});
animal.on('dragstart', function() {
this.moveToTop();
animalLayer.draw();
});
/*
* check if animal is in the right spot and
* snap into place if it is
*/
animal.on('dragend', function() {
var outline = outlines[privKey + '_black'];
if(!animal.inRightPlace && isNearOutline(animal, outline)) {
animal.position({
x : outline.x,
y : outline.y
});
animalLayer.draw();
animal.inRightPlace = true;
if(++score >= 4) {
var text = 'You win! Enjoy your booty!';
drawBackground(background, images.beach, text);
}
// disable drag and drop
setTimeout(function() {
animal.draggable(false);
}, 50);
}
});
// make animal glow on mouseover
animal.on('mouseover', function() {
animal.image(images[privKey + '_glow']);
animalLayer.draw();
document.body.style.cursor = 'pointer';
});
// return animal on mouseout
animal.on('mouseout', function() {
animal.image(images[privKey]);
animalLayer.draw();
document.body.style.cursor = 'default';
});
animal.on('dragmove', function() {
document.body.style.cursor = 'pointer';
});
animalLayer.add(animal);
animalShapes.push(animal);
})();
}
// create animal outlines
for(var key in outlines) {
// anonymous function to induce scope
(function() {
var imageObj = images[key];
var out = outlines[key];
var outline = new Konva.Image({
image: imageObj,
x: out.x,
y: out.y
});
animalLayer.add(outline);
})();
}
stage.add(background);
stage.add(animalLayer);
drawBackground(background, images.beach, 'Ahoy! Put the animals on the beach!');
}
var sources = {
beach: 'beach.png',
snake: 'snake.png',
snake_glow: 'snake-glow.png',
snake_black: 'snake-black.png',
lion: 'lion.png',
lion_glow: 'lion-glow.png',
lion_black: 'lion-black.png',
monkey: 'monkey.png',
monkey_glow: 'monkey-glow.png',
monkey_black: 'monkey-black.png',
giraffe: 'giraffe.png',
giraffe_glow: 'giraffe-glow.png',
giraffe_black: 'giraffe-black.png'
};
loadImages(sources, initStage);
</script>
as we can see in this example Animals_on_the_Beach_Game the animal's images are drag-able and can be drop ever where.... but I want to change it in the way that it just can drop on the specific place ... what can I do ?
thank you :)
This is more of a design question, as letting go of the mouse button isn't something you can prevent. It would also be non-intuitive to keep the image attached to the mouse position as you would then need a new mouse event to associate with dropping it. What I've done for a drag and drop UI was to either (1) destroy the dropped shape, or if that wasn't an option, (2) animate the shape back (i.e. snap back) to its original position. Alternatively, you might (3) find the closest likely valid drop target and snap to that location.
First you define lionOrigin, that maybe you already have.
You have to implement the call on the dragend event of the object dragged, so let's say the lion. You have to check position of the lion in relation to the end desired position, let's call it lionDestiny. That can be done with a simple grometry: calculate the distance between to point. We do that with distanceA2B() function.
Now you can establish an offset inside wich you can snap the object, as it is close enough. If the minimal offset is not achieved, then you place the lion back on lionOrigin.
Al last, in konvajs you can use .x() and .y() to easily get or set position to lion.
Something like this:
var lionOrigin = [50,50];
var lionDestiny = [200,200];
var offset = 20;
distanceA2B(a,b) {
return Math.sqrt( ((a[0]-b[0])*(a[0]-b[0])) + ((a[1]-b[1])*(a[1]-b[1])) );
}
lion.on('dragend', (e) => {
var d = distanceA2B([lion.x(),lion.y()],lionDestiny);
if(d<offset){
lion.x(lionDestiny[0]);
lion.y(lionDestiny[1]);
}else{
lion.x(lionOrigin[0]);
lion.y(lionOrigin[1]);
}
});
Hope this helps!
It would have been better if you could explain your question more when you say you want to move any shape to a specific position. Though konva.js provides you with various events through which you can do this. For example, suppose you want to interchange the location of two shapes when you drag and move the first shape to the second and drop it there. In this case, you can use dragend event of konva. So when you move the target element to another element and drop it there, check if they are intersecting each other or not and then interchange their coordinates with each other.
Here is the function to find the intersection between two elements:
haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width ||
r2.x + r2.width < r1.x ||
r2.y > r1.y + r1.height ||
r2.y + r2.height < r1.y
);
}
And from here, you can try to understand the functionality. Though it's in nuxt.js but the events and scripts would be almost same if you are using only javascript. You can find sample code with an explanation for replacing the location of two shapes with each other. So even if you don't want to replace the locations but you want to move your target element to any position this will make you understand how to do this.

Draw a circle again after vector export

After draw a circle in my map, I exported it with:
getAsJson : function() {
var geojson = new ol.format.GeoJSON();
var features = this.vectorSource.getFeatures();
var jsonData = geojson.writeFeatures( features,{
featureProjection: ol.proj.get('EPSG:3857'),
dataProjection: ol.proj.get('EPSG:4326')
});
return jsonData;
}
and the result was:
{"type":"FeatureCollection","features":[
{"type":"Feature","geometry":{
"type":"GeometryCollection","geometries":[]
},"properties":{
"circleCenter":[-4805776.093508227,-2600749.7153150304],"circleRadius":6658.801529937424
}
}]}
This is how I take the circle center and radius:
var draw = new ol.interaction.Draw({
source: vectorSource,
type: value, // Can be Circle,Point,Line,Polygon
// No Geometry Function when type is 'Circle' (omited code to simplify)
geometryFunction: geometryFunction,
maxPoints: maxPoints
});
draw.on('drawend', function( evt ){
var geometry = evt.feature.getGeometry();
// Check the type before try to get this! (omited code to simplify)
var center = geometry.getCenter();
var radius = geometry.getRadius();
evt.feature.set('circleCenter', center );
evt.feature.set('circleRadius', radius );
});
map.addInteraction( draw );
Now I'm trying to use this JSON to draw the same circle again, but it have no geometry and this is not working (work for all other geometries like point, polygong and line so the problem is it not the code):
var features = new ol.format.GeoJSON().readFeatures( jsonData, {
featureProjection: 'EPSG:3857'
});
var vectorSource = new ol.source.Vector({
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style : customStyleFunction
});
map.addLayer( vectorLayer );
Just a note: I can see now the projection of center and radius was not changed. Must work on this too...
GeoJSON does not support Circle Geometry. So ol.format.GeoJSON() format doesnot convert JSON to ol.Feature object. So write a custom method which consumes the JSON data and creates a Circle geometry
var featuresJson = geoJson.features;
for ( var i=0; i<featuresJson.length; i++ ) {
var radius = featuresJson[i].properties.circleRadius;
var center = featuresJson[i].properties.circleCenter;
var feature = new ol.Feature(new ol.geom.Circle(center,radius);
vectorSource.addFeature(feature);
}
I think this can help me somehow... will see.
map.getViewport().addEventListener("dblclick", function(e) {
var coordinate = map.getEventCoordinate(e);
vectorSource.addFeature(new ol.Feature(new ol.geom.Circle(coordinate, dist)));
});

URL parameters for Openlayers 3 to zoom to location?

I have an OL3 web application and I am wondering if it is possible to include URL parameters (such as coordinate values) for which the application can parse and open up at a specific location?
For example http://mywebsiteaddress?x=longitudevalue&y=latitudevalue
Is this something that can be done using OL3?
Sure, see: http://openlayers.org/en/latest/examples/permalink.html for an example (uses an anchor instead of url parameters but the idea is the same).
I did not like the openlayers permalink example because it uses map units and not well-known latitudes and longitudes. Sp I wrote my own code to hand over latlon coordinates, zoom and set a marker to it:
function getURLParameter(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null;
}
var mzoom=12;
var mlat = Number(getURLParameter('mlat'));
var mlon = Number(getURLParameter('mlon'));
var mzoom = Number(getURLParameter('zoom'));
var marker = 1
if (mlat == 0 || mlon == 0) {
mlat = 51.5; mlon = 7.0; mzoom=12; marker=0 //Default location
}
if (mzoom == 0 ) { mzoom=12 //Default zoom
}
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
closer.onclick = function() {
container.style.display = 'none';
closer.blur();
return false;
};
var overlayPopup = new ol.Overlay({
element: container
});
var expandedAttribution = new ol.control.Attribution({
collapsible: false
});
var map = new ol.Map({
controls: ol.control.defaults({attribution:false}).extend([
expandedAttribution
]),
target: document.getElementById('map'),
renderer: 'canvas',
overlays: [overlayPopup],
layers: layersList,
view: new ol.View({
center: ol.proj.fromLonLat([mlon, mlat]),
zoom: mzoom,
maxZoom: 18, minZoom: 8
})
});
if (marker == 1) {
var vectorLayer = new ol.layer.Vector({
source:new ol.source.Vector({
features: [new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([parseFloat(mlon), parseFloat(mlat)], 'EPSG:4326', 'EPSG:3857')),
})]
}),
style: new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 0.5],
anchorXUnits: "fraction",
anchorYUnits: "fraction",
src: "marker.svg"
})
})
});
map.addLayer(vectorLayer);
}
I used the output of the qgis2web plugin and modified the file qgis2web.js as above.

Openlayers 3: Drawing grid lines (graticule) with predefined units on the custom static image

I am trying to draw custom x-y axes grid lines on top of a static image, i.e. image pixels rather than lattitude and longitudes. Ideally, the grid lines should be redrawn dynamically when I drag/zoom/scroll the image, just like the x-y ruler bars in Photoshop.
I came across the following code example, which provides a custom projection function to directly map image pixel coordinates to map coordinates.
http://openlayers.org/en/latest/examples/static-image.html
// Map views always need a projection. Here we just want to map image
// coordinates directly to map coordinates, so we create a projection that uses
// the image extent in pixels.
var extent = [0, 0, 1024, 968];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
I tried to append the following code to the script. However, the ol.Graticule class seems to be incompatible with the custom ol.proj.Projection definition.
http://openlayers.org/en/latest/examples/graticule.html
// Create the graticule component
var graticule = new ol.Graticule({
// the style to use for the lines, optional.
strokeStyle: new ol.style.Stroke({
color: 'rgba(255,120,0,0.9)',
width: 2,
lineDash: [0.5, 4]
})
});
graticule.setMap(map);
What's wrong with the above code?
P.S. I am aware of the Openseadragon API which provides a dynamic scalebar. However, I wish to stick to Openlayers API because I also have an extra map layer of anchor points at predefined locations on the static image.
I had the same problem. For this to work I created a Vector Layer, (where axis are drawn).
To draw the axis, I need to listen to View changes.
Whenever the view changes, calculate the actual extent for the view.
With extent information and ([width, height] of the image, you can then draw axis)
let listenerAxis = null,
w = 0,
h = 0
const xAxisStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
})
})
const yAxisStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 2
})
})
const ImageLayer = new ol.layer.Image()
const AxisLayer = new ol.layer.Vector({ source: new ol.source.Vector() })
AxisLayer.setStyle((feature, resolution) => {
if(feature.getProperties().axis == 'x') {
return xAxisStyle
}
return yAxisStyle
})
const renderer = new ol.Map({
target: 'map',
layers: [ImageLayer]
})
AxisLayer.setMap(renderer)
processFile('https://i2.wp.com/beebom.com/wp-content/uploads/2016/01/Reverse-Image-Search-Engines-Apps-And-Its-Uses-2016.jpg?resize=640%2C426')
function removeAxis() {
AxisLayer.getSource().clear()
ol.Observable.unByKey(listenerAxis)
listenerAxis = null
}
function drawAxis() {
function draw(){
AxisLayer.getSource().clear()
const extent = renderer.getView().calculateExtent()
const [xmin, ymin, xmax, ymax] = extent
// Eje X
const axisX = new ol.geom.LineString([ [xmin, h / 2], [xmax, h / 2] ])
const axisY = new ol.geom.LineString([ [w / 2, ymin], [w / 2, ymax] ])
const featureX = new ol.Feature({ geometry: axisX, axis: 'x' })
const featureY = new ol.Feature({ geometry: axisY, axis: 'y' })
AxisLayer.getSource().addFeatures([featureX, featureY])
}
listenerAxis = renderer.getView().on('change', draw)
draw()
}
async function processFile(path) {
ImageLayer.setSource()
removeAxis()
if(!path) {
return
}
const [wi, hi] = await readImage(path)
w = wi
h = hi
const source = getImageStatic(path, w, h)
const view = getViewForImage(w, h)
ImageLayer.setSource(source)
renderer.setView(view)
drawAxis()
}
// Some helpers
function readImage(localPath) {
const img = document.createElement('img')
return new Promise((res, rej) => {
img.src = localPath
img.addEventListener('load', (event) => {
const { naturalWidth, naturalHeight } = img
console.log('img', naturalWidth, naturalHeight)
res([naturalWidth, naturalHeight])
})
})
}
function getViewForImage(w, h) {
return new ol.View({
center: [w / 2, h / 2],
zoom: 2,
projection: new ol.proj.Projection({
extent: [0, 0, w, h],
units: 'pixels'
}),
extent: [0, 0, w, h]
})
}
function getImageStatic(path, w, h) {
return new ol.source.ImageStatic({
url: path,
imageExtent: [0, 0, w, h]
})
}
#map {
width: 100%;
height: 100%;
background: grey;
}
<link href="https://openlayers.org/en/v4.6.5/css/ol.css" rel="stylesheet"/>
<script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
<div id="map"></div>

How to animate a line string between 2 points in OpenLayers 3 map?

I want to draw a line between multiple points from an array of coordinates.
My code looks like :
<button onclick="drawAnimatedLine(new ol.geom.Point(6210355.674114,2592743.9994331785), new ol.geom.Point(8176927.537835015,2255198.08252584), 50, 2000);">Draw Line</button>
And my js looks like :
function drawAnimatedLine(startPt, endPt, steps, time, fn) {
var style = {
strokeColor: "#0500bd",
strokeWidth: 15,
strokeOpacity: 0.5,
strokeColor: '#0000ff'
};
var directionX = (endPt.x - startPt.x) / steps;
var directionY = (endPt.y - startPt.y) / steps;
var i = 0;
var prevLayer;
var lineDraw = setInterval(function () {
console.log("Inside Animate Line");
if (i > steps) {
clearInterval(lineDraw);
if (fn)
fn();
return;
}
var newEndPt = new ol.geom.Point(startPt.x + i * directionX, startPt.y + i * directionY);
var line = new ol.geom.LineString([startPt, newEndPt]);
var fea = new ol.Feature({
geometry:line,
style: style
});
var vec = new ol.layer.Vector();
vec.addFeatures([fea]);
map.addLayer(vec);
if(prevLayer)
{
map.removeLayer(prevLayer);
}
prevLayer = vec;
i++;
}, time / steps);
}
Note : Coordinates will be dynamic but for testing I've passed the sample data in onclick of the button. Please do try to sort out this issue as soon as possible.

Resources