Responsive svg marker icons in googlemaps - javascript

I have a map (example here), with customized svg icon markers:
var icon = {
path: "M-10,0a10,10 0 1,0 20,0a10,10 0 1,0 -20,0",
fillColor: '#FF0000',
fillOpacity: .6,
anchor: new google.maps.Point(0, 0),
strokeWeight: 0,
scale: 1
}
If you zoom in the map you will realize icon is immutable size,
there is a way to resize it when zooming? (zoom in makes icon bigger and zoom out smaller)
I've found how to make svg icons responsive, but this forces me to add the svg into an element and use css.
If I have lots of markers in a map, I know I can group them, but this is not what I'm searching.

From your initialize method you can call the setZoomChangedEvent method as given below. This is just for proof of concept. You will need to decide the scale that you want depending on the zoom level and change the value of the variable "scale" inside the if-else-if ladder. The method checks the zoom level every time the zoom is changed and the scale is calculated according to the zoom. As geocodezip has mentioned in the comments there could be an impact on performance if there are a lot of markers on the map.
function setZoomChangedEvent()
{
var prevscale = 3;
var scale = 0;
google.maps.event.addListener(map, 'zoom_changed', function() {
var zoom = map.getZoom();
if(zoom < 3)
{
scale = 2;
}
else if(zoom < 5 )
{
scale = 3;
}
else if(zoom < 10)
{
scale = 4;
}
else if(zoom < 15)
{
scale = 5;
}
else if(zoom < 20)
{
scale = 6;
}
if(scale!=prevscale)
{
var len = markers.length;
for(var j=0; j<len; j++)
{
var icon = markers[j].getIcon();
if(icon.hasOwnProperty("scale"))
{
icon.scale = scale;
markers[j].setIcon(icon);
}
}
}
prevscale = scale;
});
}

Related

Adding Arrows or Overlay animation in Flight Animation example in OpenLayers 6

I want to add moving arrows or overlay animation in the Flights Animation example in OpenLayers 6.
I tried doing the overlay moving animation with JavaScript setInterval(), but so far I have only succeeded in animating a single LineString, that too after the line is finished drawing. I wanted to add the moving animation as the line is being drawn, kind of like tracing the LineString's path.
Can someone please help me with this?
Following is the code snippet where I have tried to add the moving animation:
var markerEl = document.getElementById('geo-marker');
var marker = new Overlay({
positioning: 'center-center',
offset: [0, 0],
element: markerEl,
stopEvent: false
});
map.addOverlay(marker);
function animateFlights(event) {
var coords;
var vectorContext = getVectorContext(event);
var frameState = event.frameState;
var features = flightSource.getFeatures();
for (var i = 0; i < features.length; i++) {
var feature = features[i];
if (!feature.get('finished')) {
coords = feature.getGeometry().getCoordinates();
var elapsedTime = frameState.time - feature.get('start');
var elapsedPoints = elapsedTime * pointsPerMs;
if (elapsedPoints >= coords.length) {
feature.set('finished', true);
}
var maxIndex = Math.min(elapsedPoints, coords.length);
var currentLine = new LineString(coords.slice(0, maxIndex));
vectorContext.setStyle(strokeStyle1);
vectorContext.drawGeometry(currentLine);
if (feature.get('finished')) {
var interval = setInterval(
function () { return animatePath(coords, interval) }, 10);
}
}
}
map.render();
}
function animatePath(path, clearInterval) {
if (i == path.length) {
stopAnimatePath(clearInterval);
}
marker.setPosition(path[i]);
i = i + 1;
}
function stopAnimatePath(clearInterval) {
clearInterval(clearInterval);
}
Here is a link to a snapshot of how my app looks right now
Trace your LineString
It should be enough to set your map center to the last point of your LineString if you update often enough
map.getView().setCenter(lastPoint)
If it gets laggy use
var pan = ol.animation.pan({
source: map.getView().getCenter()
});
map.beforeRender(pan);
map.getView().setCenter(lastPoint);
Draw arrows
To draw arrows on your LineString you can use the following style
var styleFunction = function (feature) {
var geometry = feature.getGeometry();
var styles = [
// linestring
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#000',
width: 2
})
})
];
geometry.forEachSegment(function (start, end) {
var dx = end[0] - start[0];
var dy = end[1] - start[1];
var rotation = Math.atan2(dy, dx);
styles.push(new ol.style.Style({
geometry: new ol.geom.Point(end),
image: new ol.style.RegularShape({
fill: new ol.style.Fill({color: '#000'}),
points: 3,
radius: 8,
rotation: -rotation,
angle: Math.PI / 2 // rotate 90°
})
}));
});
return styles;
};
more details: https://stackoverflow.com/a/58237497/546526

Tiling contiguous polygons in Google Maps

I'm trying to draw a hexagonal grid in Google Maps. I've come up with a solution based off this answer which looks fine at higher zooms, but when zoomed further out I find that the classic "orange-peel" problem occurs: The hexagons no longer fit together like they should:
I'm using this rather cool geodesy library to calculate hexagon centers based on an ellipsoidal model (since a 2d model clearly doesn't work on a real-world map) but it's still looking pretty bad when zoomed out.
Preferably, I'd like to draw the hexagons in such a way that they are exactly the same shape and size on screen.
Here's the code I've been working with, also available as a Plunker here. I've tried calculating the vertices of each polygon using the same geodesy library that I'm using to calculate the polygon centers, but it still doesn't look right when zoomed out.
var hexgrid = [];
function initialize(){
// Create the map.
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 51.5, lng: 0},
scrollwheel: true,
zoom: 8
});
// This listener waits until the map is done zooming or panning,
// Then clears all existing polygons and re-draws them.
map.addListener('idle', function() {
// Figure out how big our grid needs to be
var spherical = google.maps.geometry.spherical,
bounds = map.getBounds(),
cor1 = bounds.getNorthEast(),
cor2 = bounds.getSouthWest(),
cor3 = new google.maps.LatLng(cor2.lat(), cor1.lng()),
cor4 = new google.maps.LatLng(cor1.lat(), cor2.lng()),
diagonal = spherical.computeDistanceBetween(cor1,cor2),
gridSize = diagonal / 20;
// Determine the actual distance between tiles
var d = 2 * gridSize * Math.cos(Math.PI / 6);
// Clear all the old tiles
hexgrid.forEach(function(hexagon){
hexagon.setMap(null);
});
hexgrid = [];
// Determine where the upper left-hand corner is.
bounds = map.getBounds();
ne = bounds.getNorthEast();
sw = bounds.getSouthWest();
var point = new LatLon(ne.lat(), sw.lng());
// ... Until we're at the bottom of the screen...
while(point.lat > sw.lat()){
// Keep this so that we know where to return to when we're done moving across to the right
leftPoint = new LatLon(point.lat, point.lon).destinationPoint(d, 150).destinationPoint(d, 210).destinationPoint(d, 270).destinationPoint(d, 90)
step = 1;
while(point.lon < ne.lng()){
// Use the modulus of step to determing if we want to angle up or down
if (step % 2 === 0){
point = new LatLon(point.lat, point.lon).destinationPoint(d, 30);
} else {
point = new LatLon(point.lat, point.lon).destinationPoint(d, 150);
}
step++; // Increment the step
// Draw the hexagon!
// First, come up with the corners.
vertices = [];
for(v = 1; v < 7; v++){
angle = v * 60;
vertex = point.destinationPoint(d / Math.sqrt(3), angle);
vertices.push({lat: vertex.lat, lng: vertex.lon});
}
// Create the shape
hexagon = new google.maps.Polygon({
map: map,
paths: vertices,
strokeColor: '#090',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#090',
fillOpacity: 0.1,
draggable: false,
});
// Push it to hexgrid so we can delete it later
hexgrid.push(hexagon)
}
// Return to the left.
point = leftPoint;
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Please consider that Google Maps is in Mercator Projection.
You have to compensate for the sphere of the globe on the projection.
https://en.wikipedia.org/wiki/Mercator_projection

Animating SVG polygons

I wrote a code to drawing polygons:
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', window.innerHeight);
document.querySelector('#bg').appendChild(svg);
for(var x = 0; x < polygons.length; x++){
var polygon = document.createElementNS(svg.namespaceURI, 'polygon');
polygon.setAttribute('points', polygons[0 + x]);
polygon.setAttribute('fill', polygons[0 + x][1]);
svg.appendChild(polygon);
}
My full code with polygon points:
http://codepen.io/anon/pen/WrqrbB
I would like to animate this polygons similar to this animation:
http://codepen.io/zessx/pen/ZGBMXZ
How to animate my polygons?
You can
call an animation function to manipulate your coordinate values as desired,
convert them to a string, e.g. using .join(),
send the resulting string back to the polygon as its points attribute value, redrawing the shape (as you were already doing when you initially created your shapes), and
have the animation function, when it is finished, call itself again at a reasonable built-in time-delay using requestAnimationFrame.
The following snippet gives a basic idea of what can be done.
(Note that I've redefined the array polygons in my example so that it is different from what you had, but that was done for the sake of simplicity in this example.)
var svg = document.getElementsByTagName("svg")[0];
var polygons = [], numSteps = 100, stepNum = 0;
var coords = [
[40, 20, 80, 20, 80, 60, 40, 60],
[140, 20, 180, 20, 160, 50]
];
for (var x = 0; x < coords.length; x++) {
polygons[x] = document.createElementNS(svg.namespaceURI, 'polygon');
polygons[x].setAttribute('points', coords[x].join());
svg.appendChild(polygons[x]);
}
function anim() {
for (var x = 0; x < coords.length; x++) {
coords[x] = coords[x].map(function(coord) {
return coord + 4 * (Math.random() - 0.5);
});
polygons[x].setAttribute('points', coords[x].join());
stepNum += 1;
}
if (stepNum < numSteps) requestAnimationFrame(anim);
}
anim();
<svg></svg>
UPDATE The above snippet shows generally how to animate a polygon. In your case, however, there is a further issue. On your codepen demo, it is clear that you have hard-coded the point coordinates for each polygon separately. Thus, if you want to move one point, you're going to have to update coordinates in at least 2 if not more places, for every polygon that touches that point.
A better approach would be to create a separate array of all points and then define each polygon with respect to that array. (This is similar to how things are sometimes done in 3D graphics, e.g. WebGL.) The following code snippet demonstrates this approach.
var svg = document.getElementsByTagName("svg")[0];
var polyElems = [], numSteps = 100, stepNum = 0;
var pts = [[120,20], [160,20], [200,20], [240,20], [100,50], [140,50], [180,50], [220,50], [260,50], [120,80], [160,80], [200,80], [240,80]];
var polyPts = [[0,1,5], [1,2,6], [2,3,7], [0,4,5], [1,5,6], [2,6,7], [3,7,8], [4,5,9], [5,6,10], [6,7,11], [7,8,12], [5,9,10], [6,10,11], [7,11,12]];
for (var x = 0; x < polyPts.length; x++) {
polyElems[x] = document.createElementNS(svg.namespaceURI, 'polygon');
polyElems[x].setAttribute('fill', '#'+Math.floor(Math.random()*16777215).toString(16));
// random hex color routine from http://www.paulirish.com/2009/random-hex-color-code-snippets/
drawPolygon(x);
}
function anim() {
pts = pts.map(function(pt) {
return pt.map(function(coord) {
return coord + 3 * (Math.random() - 0.5); // move each point
});
});
for (var x = 0; x < polyPts.length; x++) {drawPolygon(x);}
stepNum += 1;
if (stepNum < numSteps) requestAnimationFrame(anim); // redo anim'n until all anim'n steps done
}
anim(); // start the animation
function drawPolygon(x) {
var ptNums = polyPts[x];
var currCoords = [pts[ptNums[0]], pts[ptNums[1]], pts[ptNums[2]]].join();
// creates a string of coordinates; note that [[1,2],[3,4],[5,6]].join() yields "1,2,3,4,5,6"
polyElems[x].setAttribute('points', currCoords);
svg.appendChild(polyElems[x]);
}
<svg></svg>

Is it possible to remove the border from RouteBoxer-boxes?

EDIT This question has been answered but I've clarified it for future readers.
RouteBoxer provides a solution of 'boxing in' a route in Google maps so that I can list various points of interest along that route. This technic though creates borders around each box presented on the map. I've attached an image to describe what it looks like.
The code produced to create RouteBoxer looks like this:
<script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/routeboxer/src/RouteBoxer.js"></script>
var directionService = new google.maps.DirectionsService();
var rboxer = new RouteBoxer();
var distance = 20; // km
directionService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
// Box the overview path of the first route
var path = result.routes[0].overview_path;
var boxes = routeBoxer.box(path, distance);
for (var i = 0; i < boxes.length; i++) {
var bounds = box[i];
// Perform search over this bounds
}
}
});
Is it possible to remove the borders surrounding each box on the map?
You have full control. Here is the example above in a fiddle, without black borders -> http://jsfiddle.net/ftgr8dyp/ Look at the function drawBoxes() :
function drawBoxes(boxes) {
boxpolys = new Array(boxes.length);
for (var i = 0; i < boxes.length; i++) {
boxpolys[i] = new google.maps.Rectangle({
bounds: boxes[i],
fillOpacity: 0,
strokeOpacity: 1.0,
strokeColor: '#000000', //<-- change color
strokeWeight: 0, //<-- change strokeWeight from 1 to 0
map: map
});
}
}
It is standard google.maps.Rectangle's you can style as you are used to. There is nothing in the RouterBoxer-code that forces certain design or styles.

How to Change Color of Symbols in PaperJs

I don't know how to create a jsfiddle for Paper.js since I keep getting "domain policy" restrictions; but you can test by going here and clicking run: http://paperjs.org/tutorials/animation/creating-animations/#moving-multiple-items
I have the following code:
// The amount of circles we want to make:
var count = 50;
// Create a symbol, which we will use to place instances of later:
var point = new Point(20, 20);
var size = new Size(60, 60);
var path = new Path.Rectangle({
point: point,
size: size,
fillColor: 'grey',
strokeColor: 'black'
});
var symbol = new Symbol(path);
// Place the instances of the symbol:
for (var i = 0; i < count; i++) {
// The center position is a random point in the view:
var center = Point.random() * view.size;
var placedSymbol = symbol.place(center);
var placedSymbol = symbol.place(center);
placedSymbol.scale(i / count);
if (i % 5 == 0) {
placedSymbol.style = {
fillColor: new Color(1, 0, 0),
strokeColor: 'black',
strokeWidth: 5
};
}
}
console.log(project.activeLayer.children[0]);
// The onFrame function is called up to 60 times a second:
function onFrame(event) {
// Run through the active layer's children list and change
// the position of the placed symbols:
for (var i = 0; i < count; i++) {
var item = project.activeLayer.children[i];
// Move the item 1/20th of its width to the right. This way
// larger circles move faster than smaller circles:
item.position.x += item.bounds.width / 100;
//if (i % 5 == 0)
//item.fillColor = new Color(1, 0, 0);
// If the item has left the view on the right, move it back
// to the left:
if (item.bounds.left > view.size.width) {
item.position.x = -item.bounds.width;
}
}
}
I know I can just change the color of the path like this:
var path = new Path.Rectangle({
point: point,
size: size,
fillColor: 'grey',
strokeColor: 'black'
});
But, I want to change the color of only specific rectangles which are divisible by five:
if (i % 5 == 0) {
placedSymbol.style = {
fillColor: new Color(1, 0, 0),
strokeColor: 'black',
strokeWidth: 5
};
}
But, the fill color never changes! What is the proper way to change the fill color/ style of only certain symbols?
The answer for me was to not use symbols at all, just use paths like so:
// The amount of circles we want to make:
var count = 150;
// Create a symbol, which we will use to place instances of later:
// Place the instances of the symbol:
for (var i = 0; i < count; i++) {
// The center position is a random point in the view:
var path = new Path.Circle({
center: Point.random() * view.size,
radius: i / count + 0.5,
fillColor: 'white',
strokeColor: 'black'
});
if (i % 10 == 0)
path.style.fillColor = '#eee';
}
// The onFrame function is called up to 60 times a second:
function onFrame(event) {
// Run through the active layer's children list and change
// the position of the placed symbols:
for (var i = 0; i < count; i++) {
var item = project.activeLayer.children[i];
// Move the item 1/20th of its width to the right. This way
// larger circles move faster than smaller circles:
item.position.x += item.bounds.width / 300;
// If the item has left the view on the right, move it back
// to the left:
if (item.bounds.left > view.size.width) {
item.position.x = -item.bounds.width;
}
}
}
This is a duplicate. Someone gave the right answer over there:
This is by design. You cannot change the visual properties of an instance of a Symbol. To change the color of the item, you can create multiple copies of the path by using path.clone().

Categories