Calculate the area of a polygon with latitude and longitude - google-maps

I have this code, written using this: source1 and this: source 2
public static double CalculatePolygonArea(IList<GpsLocation> coordinates)
{
double area = 0;
if (coordinates.Count > 2)
{
for (var i = 0; i < coordinates.Count-1; i++)
{
GpsLocation p1, p2;
p1 = coordinates[i];
p2 = coordinates[i + 1];
area += ToRad(p2.Longitude - p1.Longitude) * (2 + Math.Sin(ToRad(p1.Latitude))
+ Math.Sin(ToRad(p2.Latitude)));
area = area * R * R / 2;
}
}
return Math.Abs(area);
}
Here is my test code:
[Fact]
public void GpsPolygonAreaTest()
{
var poly = new List<GpsLocation>();
var p1 = new GpsLocation(0, 0);
poly.Add(p1);
var p2 = GpsHelper.CreateLocationBasedOnBearingDistance(p1, 5, 100);
poly.Add(p2);
var p3 = GpsHelper.CreateLocationBasedOnBearingDistance(p2, 95, 100);
poly.Add(p3);
var p4 = GpsHelper.CreateLocationBasedOnBearingDistance(p3, 185, 100);
poly.Add(p4);
poly.Add(p1);
var area = GpsHelper.CalculatePolygonArea(poly);
area.Should().Be(10000);
}
I confirmed that my polygon is 100m x 100m, see image:
My test result is: Expected value to be 10000, but found 1.28153883377486E+48.
Any ideas what wrong with my code?

I'm pretty sure this statement:
area = area * R * R / 2;
should be placed after the loop over the vertices, rather than repeated on each iteration.

Related

Is it possible to specify a route and then get a list of (long,lat) by specifying a percentage of the route?

After specify a route with way points I would like to move along the route in increments (specified as a fraction of the total distance [0 to 1]) and build a list of (longitude, latitude) points.
What existing function in the API, if any, facilitate this slicing of the route?
Modifying the posted example to do straight distance (it had to be modified originally to do time...). Uses a version of the epoly library by Mike Williams, ported to v3. The key method being:
.GetPointAtDistance(metres) Returns the google.maps.LatLng of a point at the specified distance along the path.
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var polyline = null;
function createMarker(latlng, label, html) {
// alert("createMarker("+latlng+","+label+","+html+","+color+")");
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
// gmarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer({
suppressMarkers: true
});
var chicago = new google.maps.LatLng(41.850033, -87.6500523);
var myOptions = {
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: chicago
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
directionsDisplay.setMap(map);
calcRoute();
}
google.maps.event.addDomListener(window, 'load', initialize);
function calcRoute() {
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
polyline.setPath([]);
var bounds = new google.maps.LatLngBounds();
startLocation = new Object();
endLocation = new Object();
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById("directions_panel");
summaryPanel.innerHTML = "";
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i == 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
// marker = google.maps.Marker({map:map,position: startLocation.latlng});
marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
for (k = 0; k < nextSegment.length; k++) {
polyline.getPath().push(nextSegment[k]);
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
computeTotalDistance(response);
} else {
alert("directions response " + status);
}
});
}
var totalDist = 0;
var totalTime = 0;
function computeTotalDistance(result) {
totalDist = 0;
totalTime = 0;
var myroute = result.routes[0];
for (i = 0; i < myroute.legs.length; i++) {
totalDist += myroute.legs[i].distance.value;
totalTime += myroute.legs[i].duration.value;
}
totalDist = totalDist / 1000.
document.getElementById("total").innerHTML = "total distance is: " + totalDist + " km<br>total time is: " + (totalTime / 60).toFixed(2) + " minutes";
document.getElementById("totalTime").value = (totalTime / 60.).toFixed(2);
}
function putMarkerOnRoute(distance) {
// alert("Time:"+time+" totalTime:"+totalTime+" totalDist:"+totalDist+" dist:"+distance);
if (!marker) {
marker = createMarker(polyline.GetPointAtDistance(distance), "distance: " + distance, "marker");
} else {
marker.setPosition(polyline.GetPointAtDistance(distance));
marker.setTitle("distance:" + distance);
}
}
/*********************************************************************\
* *
* epolys.js by Mike Williams *
* updated to API v3 by Larry Ross *
* *
* A Google Maps API Extension *
* *
* Adds various Methods to google.maps.Polygon and google.maps.Polyline *
* *
* .Contains(latlng) returns true is the poly contains the specified *
* GLatLng *
* *
* .Area() returns the approximate area of a poly that is *
* not self-intersecting *
* *
* .Distance() returns the length of the poly path *
* *
* .Bounds() returns a GLatLngBounds that bounds the poly *
* *
* .GetPointAtDistance() returns a GLatLng at the specified distance *
* along the path. *
* The distance is specified in metres *
* Reurns null if the path is shorter than that *
* *
* .GetPointsAtDistance() returns an array of GLatLngs at the *
* specified interval along the path. *
* The distance is specified in metres *
* *
* .GetIndexAtDistance() returns the vertex number at the specified *
* distance along the path. *
* The distance is specified in metres *
* Returns null if the path is shorter than that *
* *
* .Bearing(v1?,v2?) returns the bearing between two vertices *
* if v1 is null, returns bearing from first to last *
* if v2 is null, returns bearing from v1 to next *
* *
* *
***********************************************************************
* *
* This Javascript is provided by Mike Williams *
* Blackpool Community Church Javascript Team *
* http://www.blackpoolchurch.org/ *
* http://econym.org.uk/gmap/ *
* *
* This work is licenced under a Creative Commons Licence *
* http://creativecommons.org/licenses/by/2.0/uk/ *
* *
***********************************************************************
* *
* Version 1.1 6-Jun-2007 *
* Version 1.2 1-Jul-2007 - fix: Bounds was omitting vertex zero *
* add: Bearing *
* Version 1.3 28-Nov-2008 add: GetPointsAtDistance() *
* Version 1.4 12-Jan-2009 fix: GetPointsAtDistance() *
* Version 3.0 11-Aug-2010 update to v3 *
* *
\*********************************************************************/
// === first support methods that don't (yet) exist in v3
google.maps.LatLng.prototype.distanceFrom = function(newLatLng) {
var EarthRadiusMeters = 6378137.0; // meters
var lat1 = this.lat();
var lon1 = this.lng();
var lat2 = newLatLng.lat();
var lon2 = newLatLng.lng();
var dLat = (lat2 - lat1) * Math.PI / 180;
var dLon = (lon2 - lon1) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = EarthRadiusMeters * c;
return d;
}
google.maps.LatLng.prototype.latRadians = function() {
return this.lat() * Math.PI / 180;
}
google.maps.LatLng.prototype.lngRadians = function() {
return this.lng() * Math.PI / 180;
}
// === A method for testing if a point is inside a polygon
// === Returns true if poly contains point
// === Algorithm shamelessly stolen from http://alienryderflex.com/polygon/
google.maps.Polygon.prototype.Contains = function(point) {
var j = 0;
var oddNodes = false;
var x = point.lng();
var y = point.lat();
for (var i = 0; i < this.getPath().getLength(); i++) {
j++;
if (j == this.getPath().getLength()) {
j = 0;
}
if (((this.getPath().getAt(i).lat() < y) && (this.getPath().getAt(j).lat() >= y)) || ((this.getPath().getAt(j).lat() < y) && (this.getPath().getAt(i).lat() >= y))) {
if (this.getPath().getAt(i).lng() + (y - this.getPath().getAt(i).lat()) / (this.getPath().getAt(j).lat() - this.getPath().getAt(i).lat()) * (this.getPath().getAt(j).lng() - this.getPath().getAt(i).lng()) < x) {
oddNodes = !oddNodes
}
}
}
return oddNodes;
}
// === A method which returns the approximate area of a non-intersecting polygon in square metres ===
// === It doesn't fully account for spherical geometry, so will be inaccurate for large polygons ===
// === The polygon must not intersect itself ===
google.maps.Polygon.prototype.Area = function() {
var a = 0;
var j = 0;
var b = this.Bounds();
var x0 = b.getSouthWest().lng();
var y0 = b.getSouthWest().lat();
for (var i = 0; i < this.getPath().getLength(); i++) {
j++;
if (j == this.getPath().getLength()) {
j = 0;
}
var x1 = this.getPath().getAt(i).distanceFrom(new google.maps.LatLng(this.getPath().getAt(i).lat(), x0));
var x2 = this.getPath().getAt(j).distanceFrom(new google.maps.LatLng(this.getPath().getAt(j).lat(), x0));
var y1 = this.getPath().getAt(i).distanceFrom(new google.maps.LatLng(y0, this.getPath().getAt(i).lng()));
var y2 = this.getPath().getAt(j).distanceFrom(new google.maps.LatLng(y0, this.getPath().getAt(j).lng()));
a += x1 * y2 - x2 * y1;
}
return Math.abs(a * 0.5);
}
// === A method which returns the length of a path in metres ===
google.maps.Polygon.prototype.Distance = function() {
var dist = 0;
for (var i = 1; i < this.getPath().getLength(); i++) {
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
return dist;
}
// === A method which returns the bounds as a GLatLngBounds ===
google.maps.Polygon.prototype.Bounds = function() {
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < this.getPath().getLength(); i++) {
bounds.extend(this.getPath().getAt(i));
}
return bounds;
}
// === A method which returns a GLatLng of a point a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetPointAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
if (this.getPath().getLength() < 2) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
var p1 = this.getPath().getAt(i - 2);
var p2 = this.getPath().getAt(i - 1);
var m = (metres - olddist) / (dist - olddist);
return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);
}
// === A method which returns an array of GLatLngs of points a given interval along the path ===
google.maps.Polygon.prototype.GetPointsAtDistance = function(metres) {
var next = metres;
var points = [];
// some awkward special cases
if (metres <= 0) return points;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength()); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
while (dist > next) {
var p1 = this.getPath().getAt(i - 1);
var p2 = this.getPath().getAt(i);
var m = (next - olddist) / (dist - olddist);
points.push(new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m));
next += metres;
}
}
return points;
}
// === A method which returns the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetIndexAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
return i;
}
// === A function which returns the bearing between two vertices in decgrees from 0 to 360===
// === If v1 is null, it returns the bearing between the first and last vertex ===
// === If v1 is present but v2 is null, returns the bearing from v1 to the next vertex ===
// === If either vertex is out of range, returns void ===
google.maps.Polygon.prototype.Bearing = function(v1, v2) {
if (v1 == null) {
v1 = 0;
v2 = this.getPath().getLength() - 1;
} else if (v2 == null) {
v2 = v1 + 1;
}
if ((v1 < 0) || (v1 >= this.getPath().getLength()) || (v2 < 0) || (v2 >= this.getPath().getLength())) {
return;
}
var from = this.getPath().getAt(v1);
var to = this.getPath().getAt(v2);
if (from.equals(to)) {
return 0;
}
var lat1 = from.latRadians();
var lon1 = from.lngRadians();
var lat2 = to.latRadians();
var lon2 = to.lngRadians();
var angle = -Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2));
if (angle < 0.0) angle += Math.PI * 2.0;
angle = angle * 180.0 / Math.PI;
return parseFloat(angle.toFixed(1));
}
// === Copy all the above functions to GPolyline ===
google.maps.Polyline.prototype.Contains = google.maps.Polygon.prototype.Contains;
google.maps.Polyline.prototype.Area = google.maps.Polygon.prototype.Area;
google.maps.Polyline.prototype.Distance = google.maps.Polygon.prototype.Distance;
google.maps.Polyline.prototype.Bounds = google.maps.Polygon.prototype.Bounds;
google.maps.Polyline.prototype.GetPointAtDistance = google.maps.Polygon.prototype.GetPointAtDistance;
google.maps.Polyline.prototype.GetPointsAtDistance = google.maps.Polygon.prototype.GetPointsAtDistance;
google.maps.Polyline.prototype.GetIndexAtDistance = google.maps.Polygon.prototype.GetIndexAtDistance;
google.maps.Polyline.prototype.Bearing = google.maps.Polygon.prototype.Bearing;
html {
height: 100%
}
body {
height: 100%;
margin: 0px;
padding: 0px
}
<script type="text/javascript" src="https://maps.google.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="tools">
start:
<input type="text" name="start" id="start" value="Hyderabad" />end:
<input type="text" name="end" id="end" value="Bangalore" />
<input type="submit" onclick="calcRoute();" />
<br />distance (in km):
<input type="text" name="dist" id="dist" value="0" />
<input type="submit" onclick="putMarkerOnRoute(parseFloat(document.getElementById('dist').value)*1000);" />km total time:
<input type="text" name="totalTime" id="totalTime" value="0" />
</div>
<div id="map_canvas" style="float:left;width:70%;height:100%;"></div>
<div id="control_panel" style="float:right;width:30%;text-align:left;padding-top:20px">
<div id="directions_panel" style="margin:20px;background-color:#FFEE77;"></div>
<div id="total"></div>
</div>
This can be done with Turf, the functions you should take a look at is turf.along. You should be able to resample the path with points every x distance.
http://turfjs.org/static/docs/module-turf_along.html

How do I generate a visually distinct, non-random color from a single input number?

In Flash AS3, how would I write a function that will:
Take in an integer (a list index, for example)
return a visually distinct hex color based on that number (and will consistently return that same color given that same number)
The purpose is to provide a visually distinct color for each item in varying-length list of items. The most I expect to support is around 200, but I don't see the count going far above 20 or so for most.
Here's my quick and dirty:
public static function GetAColor(idx:int):uint {
var baseColors:Array = [0xff0000, 0x00ff00, 0xff0080, 0x0000ff, 0xff00ff, 0x00ffff, 0xff8000];
return Math.round(baseColors[idx % baseColors.length] / (idx + 1) * 2);
}
It does OK, but it would be nice to see a more distinct set of colors that are not so visually close to one another
You could go with generator of random values that supports seed, so you will be able return same color. As for color you could build it - by randomValue * 0xFFFFFF, where randomValue between 0 and 1. And exclude values (colors) that are close.
Second option: build palette of 200 colors with step - 0xFFFFFF / 200 and shuffle palette with predefined logic, so you will have same colors.
Third option: as for really distinct colors, you could go with big jumps in every channel. Example: 0xFF * 0.2 - 5 steps in every channel.
Fourth option: go with HSV. It's easy to understand(watch image, rotate hue from 0 to 360, change saturation and value from 0 to 100) how to manipulate parameters to get distinct color:
//Very simple demo, where I'm only rotating Hue
var step:uint = 15;
var position:uint = 0;
var colors:Array = [];
for (; position < 360; position += step) {
colors.push(HSVtoRGB(position, 100, 100));
}
//Visualisation for demo
var i:uint, len:uint = colors.length, size:uint = 40, shape:Shape, posX:uint, posY:uint;
for (i; i < len; ++i) {
shape = new Shape();
shape.graphics.beginFill(colors[i]);
shape.graphics.drawRect(0, 0, size, size);
addChild(shape);
shape.x = posX;
shape.y = posY;
posX += size;
if (posX + size >= stage.stageWidth) {
posX = 0;
posY += size;
}
}
public function HSVtoRGB(h:Number, s:Number, v:Number):uint {
var r:Number = 0;
var g:Number = 0;
var b:Number = 0;
var tempS:Number = s / 100;
var tempV:Number = v / 100;
var hi:int = Math.floor(h / 60) % 6;
var f:Number = h / 60 - Math.floor(h / 60);
var p:Number = (tempV * (1 - tempS));
var q:Number = (tempV * (1 - f * tempS));
var t:Number = (tempV * (1 - (1 - f) * tempS));
switch (hi) {
case 0:
r = tempV;
g = t;
b = p;
break;
case 1:
r = q;
g = tempV;
b = p;
break;
case 2:
r = p;
g = tempV;
b = t;
break;
case 3:
r = p;
g = q;
b = tempV;
break;
case 4:
r = t;
g = p;
b = tempV;
break;
case 5:
r = tempV;
g = p;
b = q;
break;
}
return (Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255));
}
And last one, if you want go with this task like a pro, this wiki article could be helpful for you.

Building circle meter gauge

I was wondering if someone could help me with a circle meter gauage i have taken some code from a different example and i am just protypting stuff to see if i can get it to work here is a working example.
http://jsbin.com/ixuyid/28/edit
Click run with javascript
Code below
var context;
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
//use a reusable function
function drawCircle(num){
console.log(num);
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var startAngle = 0 * Math.PI;
var endAngle = num * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 5;
// line color
context.strokeStyle = 'black';
context.stroke();
}
drawCircle();
var num = 1;
setInterval(function(){
},1000);
+function(){
var ctx = new webkitAudioContext()
, url = '//kevincennis.com/sound/loudpipes.mp3'
, audio = new Audio(url)
// 2048 sample buffer, 1 channel in, 1 channel out
, processor = ctx.createJavaScriptNode(2048, 1, 1)
, meter = document.getElementById('meter')
, source
audio.addEventListener('canplaythrough', function(){
source = ctx.createMediaElementSource(audio)
source.connect(processor)
source.connect(ctx.destination)
processor.connect(ctx.destination)
audio.play()
}, false);
// loop through PCM data and calculate average
// volume for a given 2048 sample buffer
processor.onaudioprocess = function(evt){
var input = evt.inputBuffer.getChannelData(0)
, len = input.length
, total = i = 0
, rms
while ( i < len ) total += Math.abs( input[i++] )
rms = Math.sqrt( total / len )
meter.style.width = ( rms * 100 ) + '%';
context.clearRect(100,50,200,200);
drawCircle(rms);
}
}()
I seem to be having issue with the levels???
Any help
Change these two lines in the drawCircle function:
var startAngle = 0; //multiplying with 0 will result in 0
var endAngle = 360 * num * Math.PI / 180;
Your num seem to be a value between 0 and 1 so we need to add what we're using that with, here 360 degrees, then convert by using PI / 180.
The other problem is that the clearRect wasn't extended far enough so it left part of the arc uncleared to the right.
Tip: To make it look more realistic you can update your rms only when the new rms is higher, and if not just subtract a small value for each frame.
For example:
//global scope
var oldRMS = 0;
Inside your processor.onaudioprocess after vars:
if (rms > oldRMS) oldRMS = rms;
meter.style.width = ( oldRMS * 100 ) + '%';
context.clearRect(100,50,canvas.width,canvas.height);
drawCircle(oldRMS);
oldRMS -= 0.04; //speed of fallback
Modifcations:
http://jsbin.com/ixuyid/29/edit

Google Maps determine distance along Line

I am trying to determine the distance of a point along a given Polyline (from the start point) in Google maps (given that the user clicks on the Polyline and I get the point coordinates in the event).
So far, this is the only thing that comes to mind:
Iterate over all segments in the Polyline until I find one such that
d(line, point) ~= 0, keeping track of the distance covered so far.
Interpolate on the segment the point is on to find its distance
relative to the start of the segment.
Sadly, this seems rather complicated for something that should be straightforward to do.
Is there any easier way?
P.S.: I'm using API v3
So, after much searching I decided to implement the algorithm as described above. Turned out it isn't as bad as I thought. Should anyone ever land on this page, the full code is below:
var DistanceFromStart = function (/*latlng*/ markerPosition) {
var path = this.polyline.getPath();
var minValue = Infinity;
var minIndex = 0;
var x = markerPosition.lat();
var y = markerPosition.lng();
for (var i = 0; i < path.getLength() - 1; i++) {
var x1 = path.getAt(i).lat();
var y1 = path.getAt(i).lng();
var x2 = path.getAt(i + 1).lat();
var y2 = path.getAt(i + 1).lng();
var dist = pDistance(x, y, x1, y1, x2, y2);
if (dist < minValue) {
minIndex = i;
minValue = dist;
}
}
var gdist = google.maps.geometry.spherical.computeDistanceBetween;
var dinit = gdist(markerPosition, path.getAt(minIndex));
var dtotal = gdist(path.getAt(minIndex), path.getAt(minIndex + 1));
var distanceFromStart = 0;
for (var i = 0; i <= minIndex - 1; i++) {
distanceFromStart += gdist(path.getAt(i), path.getAt(i + 1));
}
distanceFromStart += dtotal * dinit / dtotal;
return distanceFromStart;
}
function pDistance(x, y, x1, y1, x2, y2) {
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = dot / len_sq;
var xx, yy;
if (param < 0 || (x1 == x2 && y1 == y2)) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
If you see anything to improve, do let me know.
If you get the coordinates for the start and end points, then use the haversine algorithm to calculate the distance you can easily find the distance between two points taking into consideration the curvature of the earth.
Here is the formula (you may need to convert in into the language you are using):
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
variable d is your distance.
Hope this helps

Iterating each pixel of a Bitmap image in ActionScript

Is it possible to iterate each pixel of a bitmap image? Eventually what I'm trying to achieve is that I need to get the coordinate values of each pixel of a bitmap image and change the color of those pixels according to their coordinate values. As I see it, I need to use the getPixels() method but I still did not understand exactly what I should do.
( too slow :) )
so this is the sae as above with a linear loop instead of 2 nested loops.
//creates a new BitmapData, with transparency, white 0xFFFFFF
var bd:BitmapData = new BitmapData( 100, 100, false, 0xFFFFFF );
//stores the width and height of the image
var w:int = bd.width;
var h:int = bd.height;
var i:int = w * h;
var x:int, y:int, col;
//decremental loop are said to be faster :)
while ( i-- )
{
//this is the position of each pixel in x & y
x = i % w;
y = int( i / w );
//gets the current color of the pixel ( 0xFFFFFF )
col = bd.getPixel( x, y );
//assign the 0xFF0000 ( red ) color to the pixel
bd.setPixel( x, y, 0xFF0000 );
}
addChild( new Bitmap( bd ) );//a nice red block
note that if you're using a bitmapData with an alpha channel (say if you load the image, the alpha will be turned on automatically ) you 'll have to use
bd.getPixel32( x, y );// returns a uint : 0xFF000000
//and
bd.setPixel32( x, y, UINT );// 0xFF000000
EDIT : I 've done a quick bench :
package
{
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.utils.getTimer;
public class pixels extends Sprite
{
private var bd:BitmapData = new BitmapData( 100, 100, false, 0xFFFFFF );
public function pixels()
{
var i:int, total:int = 100, t:int = 0;
t = getTimer();
i = total;
while( i-- )
{
whileLoop( bd );
}
trace( 'while:', getTimer() - t );
t = getTimer();
i = total;
while( i-- )
{
forLoop( bd );
}
trace( 'for:', getTimer() - t );
}
private function forLoop( bd:BitmapData ):void
{
var i:int, j:int;
var col:int;
for ( i = 0; i < bd.width; i++ )
{
for ( j = 0; j < bd.height; j++ )
{
col = bd.getPixel( i, j ); // +/- 790 ms
}
}
//for ( i = 0; i < bd.width; i++ ) for ( j = 0; j < bd.height; j++ ) col = bd.getPixel( i, j ); // +/-530 ms
//var w:int = bd.width;
//var h:int = bd.height;
//for ( i = 0; i < w; i++ ) for ( j = 0; j < h; j++ ) col = bd.getPixel( i, j ); // +/-250 ms
}
private function whileLoop( bd:BitmapData ):void
{
var w:int = bd.width;
var h:int = bd.height;
var i:int = w * h;
var col:int;
while ( i-- )
{
col = bd.getPixel( i % w, int( i / w ) ); // +/- 580 ms
}
//while ( i-- ) col = bd.getPixel( i % w, int( i / w ) ); // +/- 330 ms
}
}
}
for 100 * ( 100 * 100 ) getPixel, the fastest (on my machine) is the one-line for loop with local variables. ( +/- 250 ms ) then the one-line while( +/- 330 ms ) :)
storing local variables w and h for width and height makes the for loops twice faster :)
good to know
If, as you say, you are only setting the pixels based on their x and y, you need neither getPixel() nor getPixels()!
myBitmapData.lock();
for( var j:int = 0; j < myBitmapData.height; j++ )
{
for( var i:int = 0; i < myBitmapData.width; i++ )
{
var alpha:uint = 0xFF000000; // Alpha is always 100%
var red:uint = 0x00FF0000 * ( i / myBitmapData.width ); // Set red based on x
var green:uint = 0x0000FF00 * ( j / myBitmapData.height ); // Set green based on y
var newColor:uint = alpha + red + green; // Add the components
// Set the new pixel value (setPixel32() includes alpha, e.g. 0xFFFF0000 => alpha=FF, red=FF, green=00, blue=00)
myBitmapData.setPixel32( i, j, newColor );
}
}
myBitmapData.unlock();
If, however, you want to read the pixels' current value, let me join the speed competition.
In addition to earlier answers, here's much more speed increase!
Instead of numerous calls to getPixel(), you can use getPixels() to get a byteArray of the pixel data.
myBitmapData.lock();
var numPixels:int = myBitmapData.width * myBitmapData.height;
var pixels:ByteArray = myBitmapData.getPixels( new Rectangle( 0, 0, myBitmapData.width, myBitmapData.height ) );
for( var i:int = 0; i < numPixels; i++ )
{
// Read the color data
var color:uint = pixels.readUnsignedInt();
// Change it if you like
// Write it to the pixel (setPixel32() includes alpha, e.g. 0xFFFF0000 => alpha=FF, red=FF, green=00, blue=00)
var theX:int = i % myBitmapData.width;
myBitmapData.setPixel32( theX, ( i - theX ) / myBitmapData.width, color );
}
myBitmapData.unlock();
You need a BitmapData object. Then, it's a simple straight-forward nested loop :
var pix : int; //AS3 uses int even for uint types
for (var x:int = 0; x < myBitmapData.width; x++)
{
for (var y:int = 0; y < myBitmapData.height; y++)
{
// This'll get you the pixel color as RGB
pix = myBitmapData.getPixel(x,y);
// To change the color, use the setPixel method + the uint corresponding
// to the new color.
}
}