[GIS] How to calculate marker area on OpenLayers 3

areamarkersopenlayers

I have the following scenario: a polygon (or few) and a marker which is displaying some data of its related geometry/ies:

enter image description here

What I want is to manage the map's zoom level in order to decrease the size of the marker when it is too large for the background polygon which is hovering. I thought to calculate the area of the marker every time the zoom changes. I had this algorithm working fine for Google Maps but I can't get the solution.

This is the algorithm for Google Maps:

// Get the position of the marker
var pos = this.marker.getPosition();

// Calculate the height and the width to compare with
var height = 32 / 2;
var width = 60 / 2;

// Get the scale and the bounds taken up by the map indicator
var scale = Math.pow(2, this.map.getZoom());
var proj = this.map.getProjection();
var wc = proj.fromLatLngToPoint(pos);
var bounds = new google.maps.LatLngBounds();
var sw = new google.maps.Point(((wc.x * scale) - width) / scale, ((wc.y * scale) - height) / scale);
bounds.extend(proj.fromPointToLatLng(sw));
var ne = new google.maps.Point(((wc.x * scale) + width) / scale, ((wc.y * scale) + height) / scale);
bounds.extend(proj.fromPointToLatLng(ne));

// Get the locations delimited by the map indicator bounds
var sw = bounds.getSouthWest();
var ne = bounds.getNorthEast();
var southWest = new google.maps.LatLng(sw.lat(), sw.lng());
var northEast = new google.maps.LatLng(ne.lat(), ne.lng());
var southEast = new google.maps.LatLng(sw.lat(), ne.lng());
var northWest = new google.maps.LatLng(ne.lat(), sw.lng());

// Calcualte the area
var area = google.maps.geometry.spherical.computeArea([
    northEast, northWest, southWest, southEast
]);

And this is my adaptation to OpenLayers 3:

var pos = [item.lng, item.lat];
var scale = Math.pow(2, zoomLevel);
//var scale = 1;

// Calculate the height and the width to compare with
var height = 32 / 2;
var width = 60 / 2;

var wc = map.getPixelFromCoordinate(ol.proj.transform(pos, 'EPSG:4326', 'EPSG:3857'));

var sw = [((wc[0] * scale) - width) / scale, ((wc[1] * scale) - height) / scale];
var nw = [((wc[0] * scale) - width) / scale, ((wc[1] * scale) + height) / scale];
var se = [((wc[0] * scale) + width) / scale, ((wc[1] * scale) - height) / scale];
var ne = [((wc[0] * scale) + width) / scale, ((wc[1] * scale) + height) / scale];

var posSW = ol.proj.transform(map.getCoordinateFromPixel(sw), 'EPSG:3857', 'EPSG:4326');
var posNW = ol.proj.transform(map.getCoordinateFromPixel(nw), 'EPSG:3857', 'EPSG:4326');
var posSE = ol.proj.transform(map.getCoordinateFromPixel(se), 'EPSG:3857', 'EPSG:4326');
var posNE = ol.proj.transform(map.getCoordinateFromPixel(ne), 'EPSG:3857', 'EPSG:4326');

var extent = ol.extent.boundingExtent([posSW, posNW, posSE, posNE]);

var wgs84Sphere = new ol.Sphere(6378137);
var area = Math.abs(wgs84Sphere.geodesicArea([posSW, posNW, posNE, posSE]));

Is there any other algorithm to calculate the surface of a marker on OpenLayers 3 or a better solution for my final goal?

Best Answer

Finally I think I got the solution. The problem wasn't the algorithm itself, but the listener I was setting to execute it.

Explanation: I was executing the algorithm after an event, by this way:

var self = this;
map.getView().on('change:resolution', function() {
    self.options.onZoom(self.map.zoom);
});

The code I shown in my question was inside the self.options.onZoom function. But this was causing weird behaviors in the calculation process. Then I tried with the 'moveend' listner and it worked fine:

var self = this;
map.on('moveend', function(evt) {
    self.options.onZoom(self.map.zoom);
});

So just with this change I reached my goal but after investigate a little more, I saw an ol.View method which simplifies more the algorithm. That method is calculateExtent which needs an array with the width and the height of the element to measure. It would be something like:

map.getView().calculateExtent([width, height]);

Finally, my function looks like this:

// Calculate the height and the width to compare with
var height = 32;
var width = 60;
var extent = map.getView().calculateExtent([width, height]);

var posSW = ol.proj.transform([extent[0], extent[1]], 'EPSG:3857', 'EPSG:4326');
var posNW = ol.proj.transform([extent[0], extent[3]], 'EPSG:3857', 'EPSG:4326');
var posSE = ol.proj.transform([extent[2], extent[1]], 'EPSG:3857', 'EPSG:4326');
var posNE = ol.proj.transform([extent[2], extent[3]], 'EPSG:3857', 'EPSG:4326');

// Object needed to process the geometries coordinates and calculate its area
var wgs84Sphere = new ol.Sphere(6378137);
var area = Math.abs(wgs84Sphere.geodesicArea([posSW, posNW, posNE, posSE])) / 1000000;

As you can see, now is a little more simple and besides I'm using the proper API function for that.