How to drawing animated openlayers linestring path? - javascript

I using openlaers map api. I want to display animated route in my path like http://jerusalem.com/map#!/tour/the_way_of_the_cross/location/abu_jaafar. How to do that? Thank you.
This is my Java Script, I get data from XML file.
<script type="text/javascript">
var zoom=13
var map; //complex object of type OpenLayers.Map
function init() {
map = new OpenLayers.Map ("map", {
controls:[
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.PanZoomBar(),
new OpenLayers.Control.LayerSwitcher(),
new OpenLayers.Control.Attribution()],
maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
maxResolution: 156543.0399,
numZoomLevels: 19,
units: 'm',
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326")
} );
// Define the map layer
// Here we use a predefined layer that will be kept up to date with URL changes
layerMapnik = new OpenLayers.Layer.OSM.Mapnik("Mapnik");
map.addLayer(layerMapnik);
layerCycleMap = new OpenLayers.Layer.OSM.CycleMap("CycleMap");
map.addLayer(layerCycleMap);
layerMarkers = new OpenLayers.Layer.Markers("Markers");
map.addLayer(layerMarkers);
// Add the Layer with the GPX Track
var lgpx = new OpenLayers.Layer.Vector("Descrizione del layer", {
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "c.php",
format: new OpenLayers.Format.GPX()
}),
style: {strokeColor: "blue", strokeWidth: 5, strokeOpacity: 0.5},
projection: new OpenLayers.Projection("EPSG:4326")
});
map.addLayer(lgpx);
}
</script>

Try the answer at Drawing animated openlayers linestring path. It seems to be quite similar.
Here is the important part of the answer:
function drawAnimatedLine(startPt, endPt, style, steps, time, fn) {
var directionX = (endPt.x - startPt.x) / steps;
var directionY = (endPt.y - startPt.y) / steps;
var i = 0;
var prevLayer;
var ivlDraw = setInterval(function () {
if (i > steps) {
clearInterval(ivlDraw);
if (fn) fn();
return;
}
var newEndPt = new OpenLayers.Geometry.Point(startPt.x + i * directionX, startPt.y + i * directionY);
var line = new OpenLayers.Geometry.LineString([startPt, newEndPt]);
var fea = new OpenLayers.Feature.Vector(line, {}, style);
var vec = new OpenLayers.Layer.Vector();
vec.addFeatures([fea]);
map.addLayer(vec);
if(prevLayer) map.removeLayer(prevLayer);
prevLayer = vec;
i++;
}, time / steps);
}
The time argument specifies how long you want the animation to last
(in milliseconds), and the steps specifies how many steps you want
to divide the animation into. fn is a callback that will be executed
when the animation is complete.

Related

MarkerClusters in OpenLayer 6.3.1 with WMS as source

I am using OpenLayer version 6.3.1 and would like to know how to do a MarkerClusters using WMS data source.
My problem is that I don't know how to link the Cluster with WMS. the WMS source is a point.shp (geometrically)
I tried to replace the cluster sources by the WMS one, but it doesn't work.
Here is my code
var ImageWMS = new ImageWMS({
url: 'http://localhost:8080/geoserver/ACCESSOINSMADA/wms',
params: {'LAYERS': 'ACCESSOINSMADA:csb2-pub', 'TILED': true},
serverType: 'geoserver',
transition: 0,
});
var ImageLayers = new ImageLayer({
source: ImageWMS,
})
var distance = document.getElementById('distance');
var count = 20000;
var features = new Array(count);
var e = 3500000;
for (var i = 0; i < count; ++i) {
var coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
features[i] = new Feature(new Point(coordinates));
}
var source = new VectorSource({
features: features,
});
var clusterSource = new Cluster({
distance: parseInt(distance.value, 10),
source: source,
});
var styleCache = {};
var clusters = new VectorLayer({
source: clusterSource,
style: function (feature) {
var size = feature.get('features').length;
var style = styleCache[size];
if (!style) {
style = new Style({
image: new CircleStyle({
radius: 10,
stroke: new Stroke({
color: '#fff',
}),
fill: new Fill({
color: '#3399CC',
}),
}),
text: new Text({
text: size.toString(),
fill: new Fill({
color: '#fff',
}),
}),
});
styleCache[size] = style;
}
return style;
},
});
A WMS output is (generally) an image, so you won't be able to change the data and cluster it in a client such as OpenLayers; to do that you'll need to fetch the data through say a WFS.
As an alternative to clustering points in a client, you can configure the WMS to serve out clustered points.
For an example see:
WMS providing clustered points, shown in OpenLayers

Here Maps polyline with altitude

I need to display different polylines from A to B. So, these lines should be distinguishable from each other. I haved tried to set polylines using pushpoint function with altitude parameter. However it is still on the ground level. And the last polyline I inserted overwrites the previous one.
Altitude value works on markers but I want to apply it on polyline.
I changed the sample code here markers with altitude as below. You can see the orange line is just on top of the gray line when you change the code with the below one. I would like both lines to be displayed like the markers you see above them.
/**
* Calculate the bicycle route.
* #param {H.service.Platform} platform A stub class to access HERE services
*/
function calculateRouteFromAtoB (platform) {
var router = platform.getRoutingService(),
routeRequestParams = {
mode: 'fastest;bicycle',
representation: 'display',
routeattributes : 'shape',
waypoint0: '-16.1647142,-67.7229166',
waypoint1: '-16.3705847,-68.0452683',
// explicitly request altitude values
returnElevation: true
};
router.calculateRoute(
routeRequestParams,
onSuccess,
onError
);
}
/**
* Process the routing response and visualise the descent with the help of the
* H.map.Marker
*/
function onSuccess(result) {
var lineString = new H.geo.LineString(),
lineString2 = new H.geo.LineString(),
routeShape = result.response.route[0].shape,
group = new H.map.Group(),
dict = {},
polyline,
polyline2;
routeShape.forEach(function(point) {
var parts = point.split(',');
var pp= new H.geo.Point(parts[0],parts[1],4000,"SL");
console.log(parts[2]);
lineString.pushLatLngAlt(parts[0], parts[1]);
lineString2.pushPoint(pp);
// normalize the altitude values for the color range
var p = (parts[2] - 1000) / (4700 - 1000);
var r = Math.round(255 * p);
var b = Math.round(255 - 255 * p);
// create or re-use icon
var icon;
if (dict[r + '_' + b]) {
icon = dict[r + '_' + b];
} else {
var canvas = document.createElement('canvas');
canvas.width = 4;
canvas.height = 4;
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(' + r + ', 0, ' + b + ')';
ctx.fillRect(0, 0, 4, 4);
icon = new H.map.Icon(canvas);
// cache the icon for the future reuse
dict[r + '_' + b] = icon;
}
// the marker is placed at the provided altitude
var marker = new H.map.Marker({
lat: parts[0], lng: parts[1], alt: parts[2]
}, {icon: icon});
var marker2 = new H.map.Marker({
lat: parts[0], lng: parts[1], alt: parts[2]-800
}, {icon: icon});
group.addObject(marker);
group.addObject(marker2);
});
polyline = new H.map.Polyline(lineString, {
style: {
lineWidth: 6,
strokeColor: '#555555'
}
});
polyline2 = new H.map.Polyline(lineString2, {
style: {
lineWidth: 3,
strokeColor: '#FF5733'
}
});
// Add the polyline to the map
map.addObject(polyline);
map.addObject(polyline2);
// Add markers to the map
map.addObject(group);
// Zoom to its bounding rectangle
map.getViewModel().setLookAtData({
bounds: polyline.getBoundingBox(),
tilt: 60
});
}
/**
* This function will be called if a communication error occurs during the JSON-P request
* #param {Object} error The error message received.
*/
function onError(error) {
alert('Can\'t reach the remote server');
}
/**
* Boilerplate map initialization code starts below:
*/
// set up containers for the map + panel
var mapContainer = document.getElementById('map'),
routeInstructionsContainer = document.getElementById('panel');
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map - this map is centered over Berlin
var map = new H.Map(mapContainer,
defaultLayers.vector.normal.map,{
center: {lat:52.5160, lng:13.3779},
zoom: 13,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
// Now use the map as required...
calculateRouteFromAtoB (platform);
Unfortunately, for now only markers support altitudes.
Polylines should follow in near future.

WebWorldWind Renderable Position Update

I am trying to "move" renderables around the web worldwind globe on an interval. To illustrate the issue I am having, I made a small example.
This works (but is inefficient):
var myVar = setInterval(myTimer, 5000);
function myTimer() {
shapesLayer.removeRenderable(shape);
shape = new WorldWind.SurfaceCircle(new WorldWind.Location(shape.center.latitude+1, shape.center.longitude), 200e3, attributes);
shapesLayer.addRenderable(shape);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
This is what I'd like to do, but the shape doesn't move:
var myVar = setInterval(myTimer, 5000);
function myTimer() {
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
Is there a flag I need to set on the renderable to make it refresh?
Here is the full SurfaceShapes.js file I've been playing with (based on this http://worldwindserver.net/webworldwind/examples/SurfaceShapes.html):
/*
* Copyright (C) 2014 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration. All Rights Reserved.
*/
/**
* Illustrates how to display SurfaceShapes.
*
* #version $Id: SurfaceShapes.js 3320 2015-07-15 20:53:05Z dcollins $
*/
requirejs(['../src/WorldWind',
'./LayerManager'],
function (ww,
LayerManager) {
"use strict";
// Tell World Wind to log only warnings.
WorldWind.Logger.setLoggingLevel(WorldWind.Logger.LEVEL_WARNING);
// Create the World Window.
var wwd = new WorldWind.WorldWindow("canvasOne");
/**
* Added imagery layers.
*/
var layers = [
{layer: new WorldWind.BMNGLayer(), enabled: true},
{layer: new WorldWind.BingAerialWithLabelsLayer(null), enabled: true},
{layer: new WorldWind.CompassLayer(), enabled: true},
{layer: new WorldWind.CoordinatesDisplayLayer(wwd), enabled: true},
{layer: new WorldWind.ViewControlsLayer(wwd), enabled: true}
];
for (var l = 0; l < layers.length; l++) {
layers[l].layer.enabled = layers[l].enabled;
wwd.addLayer(layers[l].layer);
}
// Create a layer to hold the surface shapes.
var shapesLayer = new WorldWind.RenderableLayer("Surface Shapes");
wwd.addLayer(shapesLayer);
// Create and set attributes for it. The shapes below except the surface polyline use this same attributes
// object. Real apps typically create new attributes objects for each shape unless they know the attributes
// can be shared among shapes.
var attributes = new WorldWind.ShapeAttributes(null);
attributes.outlineColor = WorldWind.Color.BLUE;
attributes.drawInterior = false;
attributes.outlineWidth = 4;
attributes.outlineStippleFactor = 1;
attributes.outlineStipplePattern = 0xF0F0;
var highlightAttributes = new WorldWind.ShapeAttributes(attributes);
highlightAttributes.interiorColor = new WorldWind.Color(1, 1, 1, 1);
// Create a surface circle with a radius of 200 km.
var shape = new WorldWind.SurfaceCircle(new WorldWind.Location(35, -120), 200e3, attributes);
shape.highlightAttributes = highlightAttributes;
shapesLayer.addRenderable(shape);
// Create a layer manager for controlling layer visibility.
var layerManger = new LayerManager(wwd);
// Now set up to handle highlighting.
var highlightController = new WorldWind.HighlightController(wwd);
var myVar = setInterval(myTimer, 5000);
function myTimer() {
//Shape doesn't move
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
//Shape "moves" but is inefficient
//shapesLayer.removeRenderable(shape);
//shape = new WorldWind.SurfaceCircle(new WorldWind.Location(shape.center.latitude+1, shape.center.longitude), 200e3, attributes);
//shapesLayer.addRenderable(shape);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}
}
);
  Document says renderables variable is readonly but I think it can be possible.
change code
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
To
index = ShapesLayer.renderables.indexOf(shape);
ShapesLayer.renderables[index].center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
I think ShapesLayer.addRenderable create anther shape.
If you think it's not good way to do you can use this way
RenderableLayer.prototype.changeRenderable= function (prevRenderable, nextRenderable) {
index = ShapesLayer.renderables.indexOf(prevRenderable);
ShapesLayer.renderables[index].center = nextRenderable;
};
main code
ShapesLayer.changeRenderable(shape, new WorldWind.Location(shape.center.latitude+1, shape.center.longitude));
Document : https://nasaworldwind.github.io/WebWorldWind/layer_RenderableLayer.js.html
If anyone else is looking at this, I found a better solution to force it to recompute the center position by setting isPrepared to false and _boundaries to undefined.
var myVar = setInterval(myTimer, 5000);
function myTimer() {
shape.isPrepared = false;
shape._boundaries = undefined;
shape.center = new WorldWind.Location(shape.center.latitude+1, shape.center.longitude);
console.log(" new pos "+shape.center.latitude + " "+shape.center.longitude);
wwd.redraw();
}

How to calculate a distance between two points along a path?

I'm using openlayers3 and I have the encoded geometry. I can get the coordinates (lat,lng) for all points in the path (around 500 points per path). Given a random point inside the path, how do I calculate the distance between the beginning of the path up to that point?
I've taken a look at turfjs and it looks very promising, but the solution I pictured using it wouldn't be very nice. Taking a random point (p1), I could discover the point (p2) of the path that is closest to p1, then generate a new polygon and calculate its total distance. It may have performance issues, although the search would be O(log n) and the new polygon O(n).
EDIT: the random point is not necessarily inside the path, it's a GPS coordinate and there's a margin for error.
EDIT 2: estimation on the number of points was off, each path has about 500 points, not 5k
Does anyone know of a better approach? I'm not very experienced with either openlayers3 nor turfjs.
As you mentioned you're using OpenLayers 3, I've done an example using OpenLayers 3, the idea is:
Get Closest Point across the LineString given a coordinate
Iterate over LineString points calculating the distance of each individual paths and see if our closest point intersects the individual path.
/* Let's Generate a Random LineString */
var length = 5000;
var minLongitude = Math.random()*-180 + 180;
var minLatitude = Math.random()*-90 + 90;
var wgs84Sphere = new ol.Sphere(6378137);
var lastPoint = [minLongitude, minLatitude]
var points = Array.from({ length })
.map( _ =>{
var newPoint = [
Math.random() * (Math.random() > 0.8 ? -.005 : .005) + lastPoint[0]
, Math.random() * (Math.random() > 0.2 ? -.005 : .005) + lastPoint[1]
]
lastPoint = newPoint;
return newPoint;
})
var distanceTotal = points
.reduce((dis, p, i)=>{
if(points[i + 1])
dis += wgs84Sphere.haversineDistance(p, points[i + 1] )
return dis;
}, 0);
console.log(distanceTotal)
var extent = new ol.extent.boundingExtent(points)
//console.log(points)
var lineString = new ol.Feature({
geometry : new ol.geom.LineString(points)
});
var source = new ol.source.Vector();
var layer = new ol.layer.Vector({ source });
source.addFeature(lineString);
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
projection : 'EPSG:4326',
center: [0, 0],
zoom: 2
})
});
map.addLayer(layer)
map.getView().fit(extent, map.getSize())
var auxLayer = new ol.layer.Vector({ source : new ol.source.Vector() })
var styleAux = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 2
})
});
var styleAuxLine = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 0.5
})
});
var styleAuxPoint = new ol.style.Style({
image : new ol.style.Circle({
radius: 5,
fill: null,
stroke: new ol.style.Stroke({color: 'black', width: 2})
})
});
var styleAuxSourcePoint = new ol.style.Style({
image : new ol.style.Circle({
radius: 3,
fill: null,
stroke: new ol.style.Stroke({color: '#00bbff', width: 0.5})
})
});
auxLayer.setStyle(function(f, r){
var type = f.getGeometry().getType();
if(type === 'LineString') return styleAux;
return styleAuxPoint;
})
map.addLayer(auxLayer);
map.on('pointermove', function(e){
if(e.dragging) return;
var coord = e.coordinate;
var distance = 0;
var pointsGeometry = [];
var sourcePointFeature = new ol.Feature({
geometry : new ol.geom.Point(coord)
});
var closestPoint = lineString.getGeometry().getClosestPoint(coord);
var lineDiffFeature = new ol.Feature({
geometry : new ol.geom.LineString([
coord, closestPoint
])
});
for(let i = 0; i< points.length - 1; i++){
var p = points[i]
var next = points[i + 1];
var subLineStringGeom = new ol.geom.LineString([ p, next ]);
pointsGeometry.push(p);
var e = 1e-10;
var extent = [ closestPoint[0] - e, closestPoint[1] - e
, closestPoint[0] + e, closestPoint[1] + e
]
if(subLineStringGeom.intersectsExtent(extent)){
//console.log(i);
pointsGeometry.push(closestPoint);
distance += wgs84Sphere.haversineDistance(p, closestPoint);
break;
}
distance += wgs84Sphere.haversineDistance(p, next);
}
console.log(closestPoint)
var cpGeometry = new ol.geom.Point(closestPoint);
var cpFeature = new ol.Feature({ geometry : cpGeometry });
var geometry = new ol.geom.LineString(pointsGeometry);
var newFeature = new ol.Feature({ geometry });
auxLayer.getSource().clear();
auxLayer.getSource().refresh();
auxLayer.getSource().addFeature(lineDiffFeature);
auxLayer.getSource().addFeature(newFeature);
auxLayer.getSource().addFeature(sourcePointFeature);
auxLayer.getSource().addFeature(cpFeature);
sourcePointFeature.setStyle(styleAuxSourcePoint);
lineDiffFeature.setStyle(styleAuxLine);
//console.log(geometry.getLength())
console.log(distance);
})
html, body, #map {
width : 100%;
height : 100%;
padding : 0px;
margin : 0px;
}
<script src="https://openlayers.org/en/v3.20.1/build/ol.js"></script>
<link href="https://openlayers.org/en/v3.20.1/css/ol.css" rel="stylesheet"/>
<div id="map" class="map" tabindex="0"></div>

minimal size for a rectangle in style

I'm writing an application where users can mark regions on a world map. Now these regions can be very small, so that it's hard to click on them when not zoomed in.
Is there a way how I can define (e.g. in the style function) that a (rectangle) feature should always be rendered with at least e.g. 10px × 10px?
Update: some code I currently use:
on the drawing side:
var draw = new ol.interaction.Draw({
source: vectorSource,
type: 'LineString',
geometryFunction: function(coordinates, geometry) {
if(!geometry) {
geometry = new ol.geom.Polygon(null);
}
var start = coordinates[0];
var end = coordinates[1];
geometry.setCoordinates([[
start,
[start[0], end[1]],
end,
[end[0], start[1]],
start
]]);
return geometry;
},
maxPoints: 2
});
draw.on('drawend', function(e) {
var extent = e.feature.getGeometry().getExtent();
extent = app.map.rlonlate(extent); // own function to convert it from map coordinates into lat/lon
// some code to save the extent to the database
});
and on the displaying side:
vectorSource.addFeature(
new ol.Feature({
geometry: ol.geom.Polygon.fromExtent(app.map.lonlate(extent)),
// … some more custom properties like a display name …
})
);
the style function:
function(feature) {
return [new ol.style.Style({
stroke: new ol.style.Stroke({
color: feature.get('mine') ? '#204a87' : '#729fcf',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, ' + (feature.get('mine') ? '0.5' : '0.2') + ')'
})
})];
}
Using a style function is a good idea. The second argument of the style function is resolution which you can use to check if your feature geometry would be smaller than e.g. 10 px at the current resolution:
var styleFn = function(feature, resolution) {
var minSizePx = 30;
var minSize = minSizePx * resolution;
var extent = feature.getGeometry().getExtent();
if (ol.extent.getWidth(extent) < minSize || ol.extent.getHeight(extent) < minSize) {
// special style for polygons that are too small
var center = new ol.geom.Point(ol.extent.getCenter(extent));
return new ol.style.Style({
geometry: center,
image: ...
} else {
// normal style
return defaultStyle;
}
};
http://jsfiddle.net/ukc0nmy2/1/

Categories