Does this polyline pass through this polygon? - google-maps

I’m trying to test whether a Google Maps polyline passes through a Google Maps polygon. Sounds simple. But I’ve searched and searched... and found no real answers.
Closest I got was this function. It works but, frustratingly, returns the occasional false positive.
//nvert = the number of points in the polygon
//vertx = an array of all the polygon's latitudes
//verty = an array of all the polygon's longitudes
//elat = the current point's latitude
//elng = the current point's longitude
function pnpoly( nvert, vertx, verty, elat, elng) {
var i, j, c = false;
for( i = 0, j = nvert-1; i < nvert; j = i++ ) {
if( ( ( verty[i] > elng ) != ( verty[j] > elng ) ) &&
( elat < ( vertx[j] - vertx[i] ) * ( elng - verty[i] ) / ( verty[j] - verty[i] ) + vertx[i] ) ) {
c = !c;
}
}
return c;
}
Before I try a whole new method (a crazy math idea that brings me back to Grade 12 calculus), I’m wondering anyone knows how to accomplish this.

I've come across a working solution.
https://github.com/albertsun/JavaScript-Geometry
This geometry package includes a function called findIntersections().
I ran an $.each loop on each polygon on my map, then pushed each point in the polygon into an array, then each point in the polyline into an array. Finally, I ran two loops and pushed the lat/lon coordinates into variables for the function. It returns empty when it doesn't find anything and returns the coordinates of intersection when it finds something.
function processPath(polyline, polygons){
$.each(polygons, function(i,polygon){
var polygonArr = [] // array for storing each point in polygon
polygon.getPaths().forEach(function(k,g){
$.each(k.b, function(l,m){
polygonArr.push({'lat':m.lat(),'lng':m.lng()});
});
});
//Get the number of points in the polyLINE
var numStops = polyline.getPath().b.length -1;
//Get the path and coordinates of the polyLINE
var polylineArr = [];
polyline.getPath().forEach(function(z,y){
polylineArr.push({'lat':z.lat(),'lng':z.lng()});
});
$.each(polygonArr, function(j, polygon){
$.each(polylineArr, function(k, polyline){
if(k+1 != polylineArr.length){
var lineCoor1x = polylineArr[k].lat;
var lineCoor1y = polylineArr[k].lng;
var lineCoor2x = polylineArr[k+1].lat;
var lineCoor2y = polylineArr[k+1].lng;
var polyCoorx = polygonArr[j].lat;
var polyCoory = polygonArr[j].lng;
if(j+1 == polygonArr.length){
// We've reached the end, go back to the start
var polyCoorNextx = polygonArr[0].lat
var polyCoorNexty = polygonArr[0].lng
} else {
// Go to the next point
var polyCoorNextx = polygonArr[j+1].lat
var polyCoorNexty = polygonArr[j+1].lng
}
if(findIntersections([[[lineCoor1x,lineCoor1y], [lineCoor2x,lineCoor2y]], [[polyCoorx,polyCoory],[polyCoorNextx,polyCoorNexty]] ]).length != 0){
whereInside[i] = i;
return;
}
}
})
})
It's probably a bit messy but it works.

Related

Get each element from array as string actionscript3

I have an array like var test:Array = new Array("a", "b", "c"); How can I write a method to get one element and make it be string each time when I call this method. i.e when I call the method, it should return only 'a' and next time return only 'b' and so on.
You can use function shift of Array,here is a link about the function array shift
var test:Array = new Array("a", "b", "c");
var firstLetter:String = test.shift();//"a"
var secondLetter:String = test.shift();//"b"
var thirdLetter:String = test.shift();//"c"
#Pan's answer is correct, but I feel the need to flag the fact that shift() ignites an extremely slow process of re-indexing your entire array. It's not something you need to concern yourself with with small arrays like in your example, but for larger arrays there's a significant performance boost if you reverse() the array first and then use pop(). I'll create a performance comparison below.
Set up our test arrays:
var shiftCopy:Array = [];
var popCopy:Array = [];
for(var i:int = 0; i < 100000; i++)
{
var rand:Number = Math.random() * i;
shiftCopy.push('a' + rand);
popCopy.push('a' + rand);
}
Run the tests:
// Using shift.
var t:int = getTimer();
while(shiftCopy.length > 0) shiftCopy.shift();
trace(getTimer() - t);
// Using reverse and pop.
t = getTimer();
popCopy.reverse();
while(popCopy.length > 0) popCopy.pop();
trace(getTimer() - t);
My results:
shift: 1651ms
pop: 19ms

Sorting a multidimentional array and moving all the same values to the last array in as3

I'm currently working on a primitive graphic for the inventory
that shows how much space is currently taken and how much theres
left.
It does this by showing 5 rows of 20 squares
(Gray = free space, yellow =space taken).
Now, my problem is that I want to find all values that
are 1 and put them in the last array.
So that the array goes from:
var myMap:Array = [
[1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0]
];
to
var myMap:Array = [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
Basically I want to sort the entire array so that all zero's come first
and all 1's get put in the last one till its full, then the second last one and so on.
I'm away I will most likely need an algorithm for this but I am hoping theres an easier way to do this. I'm usually a C# developer so AS3 is not my strongest suit.
hopefully someone understood what I ment and can help me, its 4:30 am, so I might not be as lucid as I'd like.
~Thanks
EDIT
Added the code so people can get more of a informed look:
Keep in mind most of the names and so on are placeholders, currently I just need it working.
private function _showInventoryGraphic()
{
var mapWidth = 20;
var mapHeight = 5;
var myMap:Array = [
[1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0]
];
var posX:int = 15;
var posY:int = 15;
var startPosY:int = 250;
for (var i:int=0; i<mapHeight; i++)
{
for (var u:int = 0; u < mapWidth; u++)
{
if (myMap[i][u] == 0)
{
var grayRect:Shape = new Shape;
grayRect.graphics.beginFill(0x808080);
grayRect.graphics.drawRect(posX*u, startPosY, 10,10);
grayRect.graphics.endFill();
addChild(grayRect);
}
if (myMap[i][u] == 1)
{
var yellowRect:Shape = new Shape;
yellowRect.graphics.beginFill(0xFFFF00);
yellowRect.graphics.drawRect(posX*u, startPosY, 10,10);
yellowRect.graphics.endFill();
addChild(yellowRect);
}
}
startPosY += posY;
}
}
After trying it out from a few different approaches, I think the biggest challenge is the fact that it is a 2d array.
The first attempt was similar, but like quicksort, where there were 2 pointers at either end of the 2d array and they moved 'inwards' looking for a scenario where the first pointer was a '1' and the last pointer was a '0' and swapping the two, until the pointers met in the 'middle'. I gave up on this method while trying to properly decrement and increment the linear counters in a 2d array. =b
The second attempt was by keeping track of the last index in the array that was as empty and the last index in the array that was full. Then within the addItem and removeItem methods I'd put the item in the correct spot and update the counters to avoid a sort. This seemed to work, but maybe a bit messy and perhaps harder to maintain in the future. It sorts the result in separate arrays.
In the third attempt for a 'direct sort', I think the most simple way then is just flatten the array apart in to a 2d array, sort it, then rejoin it. You would be correct in that this is not the most efficient method, though one thing to consider is how often this needs to run and how efficient it really needs to be given the context. I think a 2d array of only 100 elements is not too big of an issue to use an inefficient sort method?
Maybe someone else can come up with better/cleaner methods. =b
Below is code for what I mentioned.
Second attempt (Note: I did not test this one fully, but it seemed to work):
import flash.geom.Point;
var unsortedMap:Array = [
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0]
];
var sortedMap:Array = [
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0]
];
var rowSize:int = unsortedMap[0].length;
var rowCount:int = unsortedMap.length;
// points to the last index at start, since want to have filled items at the 'end'
var emptySpotPointer:Point = new Point(rowSize - 1, rowCount - 1);
var fullSpotPointer:Point = new Point(rowSize - 1, rowCount - 1);
function print2dArray(prefix:String, input:Array):void {
trace(prefix);
for(var row:String in input){
trace("\t"+ input[row]);
}
trace("");
}
function addItem(inputX:int, inputY:int):void {
if(unsortedMap[inputY][inputX] == 1){
trace("addItem() - Already an item here: "+ inputX +", "+ inputY);
return;
}
trace("addItem() - Adding an item: "+ inputX +", "+ inputY);
unsortedMap[inputY][inputX] = 1;
sortedMap[emptySpotPointer.y][emptySpotPointer.x] = 1;
fullSpotPointer.x = emptySpotPointer.x;
fullSpotPointer.y = emptySpotPointer.y;
if(emptySpotPointer.x == 0){
emptySpotPointer.x = rowSize - 1;
emptySpotPointer.y--;
} else {
emptySpotPointer.x--;
}
}
function removeItem(inputX:int, inputY:int):void {
if(unsortedMap[inputY][inputX] == 0){
trace("removeItem() - No item here to remove: "+ inputX +", "+ inputY);
return;
}
trace("removeItem() - Removing an item here: "+ inputX +", "+ inputY);
unsortedMap[inputY][inputX] = 0;
sortedMap[fullSpotPointer.y][fullSpotPointer.x] = 0;
if(fullSpotPointer.x == (rowSize - 1)){
fullSpotPointer.x = 0;
fullSpotPointer.y++;
} else {
fullSpotPointer.x++;
}
}
// testing stuff here
// -----------------------------------------------------------------
function addRandomitems():void {
var randomX:int = Math.floor(Math.random() * rowSize);
var randomY:int = Math.floor(Math.random() * rowCount);
addItem(randomX, randomY);
}
function removeRandomitems():void {
var randomX:int = Math.floor(Math.random() * rowSize);
var randomY:int = Math.floor(Math.random() * rowCount);
removeItem(randomX, randomY);
}
print2dArray("unsortedMap", unsortedMap);
print2dArray("sortedMap", sortedMap);
trace("Test: Adding items now ---------------------");
var counter:int = 0;
for(counter = 0; counter < 50; counter++){
addRandomitems();
print2dArray("unsortedMap", unsortedMap);
print2dArray("sortedMap", sortedMap);
}
trace("Test: Removing items now ---------------------");
for(counter = 0; counter < 50; counter++){
removeRandomitems();
print2dArray("unsortedMap", unsortedMap);
print2dArray("sortedMap", sortedMap);
}
Third attempt:
import flash.utils.getTimer;
var myMap:Array = [
[1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0],
[1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0]
];
function sort(inputArray:Array):Array {
var rowSize:int = inputArray[0].length;
var flat:Array = new Array();
// flattening the array here
for(var row:String in inputArray){
flat = flat.concat(inputArray[row]);
}
flat.sort();
var result:Array = new Array();
// recreating the original array here by cutting out 'rowSize' chunks and reforming the 2d array
while(flat.length > 0){
result.push(new Array(flat.splice(0, rowSize)));
}
return result;
}
// testing
var startTimer:int = flash.utils.getTimer();
for(var counter:int = 0; counter < 10000; counter++){
sort(myMap);
}
// Running the above 10,000 times takes 1836ms for me.
trace(flash.utils.getTimer() - startTimer);
// just to see the output:
trace(sort(myMap).join("\n"));

Static Maps: Drawing polygons with many points. (2048 char limitation)

Because there is a limitation to 2048 characters in the get request, you are not able to generate an image with Google Static Maps which contains a polygon with a great number of polygon points.
Especially if you try to draw many complex polygons on one map.
If you use Google Maps API, you will have no problem - it works very well!
But I want to have an image (jpg or png)...
So, is there any opportunity to create an image from the Google Maps API? Or any way to 'trick' the 2048 char limitation?
Thanks!
There's no way to 'trick' the character limit, but it is possible to simplify your polyline to bring the encoded polyline string below the character limit. This may or may not result in a polygon of suitable fidelity for your needs.
One additional caveat is that (to the best of my knowledge) the Static Maps API only allows a single encoded polyline to be drawn on the map (this can look like a polygon, if you either close it yourself or fill it, but it's still a polyline, not a polygon).
One option for simplifying your polyline is the Douglas Peucker algorithm. Below is an implementation which extends the google.maps.Polyline object with a simplify method.
This relies on having the Google Maps JS API loaded, which you may not want if you're using Static Maps, but the code below could easily be re-written.
google.maps.Polyline.prototype.simplify = function(tolerance) {
var points = this.getPath().getArray(); // An array of google.maps.LatLng objects
var keep = []; // The simplified array of points
// Check there is something to simplify.
if (points.length <= 2) {
return points;
}
function distanceToSegment(p, v, w) {
function distanceSquared(v, w) {
return Math.pow((v.x - w.x),2) + Math.pow((v.y - w.y),2)
}
function distanceToSegmentSquared(p, v, w) {
var l2 = distanceSquared(v, w);
if (l2 === 0) return distanceSquared(p, v);
var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) return distanceSquared(p, v);
if (t > 1) return distanceSquared(p, w);
return distanceSquared(p, { x: v.x + t * (w.x - v.x), y: v.y + t * (w.y - v.y) });
}
// Lat/Lng to x/y
function ll2xy(p){
return {x:p.lat(),y:p.lng()};
}
return Math.sqrt(distanceToSegmentSquared(ll2xy(p), ll2xy(v), ll2xy(w)));
}
function dp( points, tolerance ) {
// If the segment is too small, just keep the first point.
// We push the final point on at the very end.
if ( points.length <= 2 ) {
return [points[0]];
}
var keep = [], // An array of points to keep
v = points[0], // Starting point that defines a segment
w = points[points.length-1], // Ending point that defines a segment
maxDistance = 0, // Distance of farthest point
maxIndex = 0; // Index of said point
// Loop over every intermediate point to find point greatest distance from segment
for ( var i = 1, ii = points.length - 2; i <= ii; i++ ) {
var distance = distanceToSegment(points[i], points[0], points[points.length-1]);
if( distance > maxDistance ) {
maxDistance = distance;
maxIndex = i;
}
}
// check if the max distance is greater than our tollerance allows
if ( maxDistance >= tolerance ) {
// Recursivly call dp() on first half of points
keep = keep.concat( dp( points.slice( 0, maxIndex + 1 ), tolerance ) );
// Then on second half
keep = keep.concat( dp( points.slice( maxIndex, points.length ), tolerance ) );
} else {
// Discarding intermediate point, keep the first
keep = [points[0]];
}
return keep;
};
// Push the final point on
keep = dp(points, tolerance);
keep.push(points[points.length-1]);
return keep;
};
This has been cobbled together with the help of a couple of examples (here and here).
You can now take your original polyline and feed it through this function with increasing tolerance until the resulting encoded polyline falls below the URL length limit (which will depend on the other parameters you're passing to Static Maps).
Something like this should work:
var line = new google.maps.Polyline({path: path});
var encoded = google.maps.geometry.encoding.encodePath(line.getPath());
var tol = 0.0001;
while (encoded.length > 1800) {
path = line.simplify(tol);
line = new google.maps.Polyline({path: path});
encoded = google.maps.geometry.encoding.encodePath(path);
tol += .005;
}
Another way is to use a javascript library that can convert your content of a canvas to an image. Something like
http://html2canvas.hertzen.com/
http://www.nihilogic.dk/labs/canvas2image/
Though I am not sure about it's performance for googlemaps with overlay's.
EDIT: If you're using html2canvas, be sure to checkout this question:
https://stackoverflow.com/a/17816195/2279924
As of September 2016 the URL limit has been changed to 8192 characters in size.
https://developers.google.com/maps/documentation/static-maps/intro#url-size-restriction
There was also a feature request in public issue tracker that was marked as Fixed.

AS3: Random Point on Irregular Shape

I have a MovieClip holding an irregular shape such as this one:
I need to generate a random point on this shape.
I can use brute force by generating points within the bounding box and then hitTesting to see if they reside on the irregular shape. However, I'm sure there's a more efficient way to tackle this problem.
What is the most efficient way to generate a random point on an irregular shape?
You mentioned hitTest, but I assume you meant hitTestPoint().
If so, a function go get the random points you mention, would look a bit like this:
function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
var width:Number = target.width,height:Number = target.height;
for(var i:int = 0; i < numPoints ; i++){
var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
else i = i-1;//nope, go back one step - > retry above until it is inside
}
return points;
}
The other I hinted at in my comment involves looping through non transparent pixels in a bitmap data of your object. This method would insure you don't have many duplicates, as opposed to the previous method, but it also means, you have less control over the number of points created and there's extra memory used for creating the bitmap. Still, for documentation purposes, here is the function:
function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>();
var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
for(var i:int = 0; i < numPixels; i+=res) {
x = i%bmd.width;
y = int(i/bmd.width);
alpha = pixels[i] >>> 24;
if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
}
return points;
}
function random(from:Number,to:Number):Number {
if (from >= to) return from;
var diff:Number = to - from;
return (Math.random()*diff) + from;
}
And here'a very basic test:
var pts:Vector.<Point> = getRandomPointsInClip(mc,300);
//var pts:Vector.<Point> = getGridPointsInClip(mc,100,4);
for(var i:int = 0 ; i < pts.length; i++) drawCircle(pts[i].x,pts[i].y,3,0x009900);
function getRandomPointsInClip(target:MovieClip,numPoints:int):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>(numPoints,true);
var width:Number = target.width,height:Number = target.height;
for(var i:int = 0; i < numPoints ; i++){
var point:Point = new Point(target.x+Math.random() * width,target.y+Math.random() * height);
if(target.hitTestPoint(point.x,point.y,true)) points[i] = point;//is the random coord inside ?
else i = i-1;//nope, go back one step - > retry above until it is inside
}
return points;
}
function getGridPointsInClip(target:MovieClip,res:int,offset:Number = 3):Vector.<Point>{
var points:Vector.<Point> = new Vector.<Point>();
var x:int,y:int,alpha:int,w:int = int(target.width),h:int = int(target.height);
var bmd:BitmapData = new BitmapData(w,h,true,0x00FFFFFF);bmd.draw(target);
var pixels:Vector.<uint> = bmd.getVector(bmd.rect),numPixels:int = w*h;
for(var i:int = 0; i < numPixels; i+=res) {
x = i%bmd.width;
y = int(i/bmd.width);
alpha = pixels[i] >>> 24;
if(alpha > 0) points.push(new Point(x+random(-offset,offset),y+random(-offset,offset)));
}
return points;
}
function random(from:Number,to:Number):Number {
if (from >= to) return from;
var diff:Number = to - from;
return (Math.random()*diff) + from;
}
function drawCircle(x:Number,y:Number,radius:Number,color:uint):void{
graphics.lineStyle(1,color);
graphics.drawCircle(x-radius,y-radius,radius);
}
HTH
If you think of some non-blob like shapes, it's clear the check random pixel, try again method isn't really a good way. The bounding box area could be huge compared to the shape area.
What you could do to improve the effectiveness is getting a vector of the BitmapData of the shape. It should contain all pixels of the bounding box. Update - it would be nice now if we could pick a random point, and remove it from the vector if it isn't inside the shape. Unfortunately the vector only contains the pixels' colour, not the position which is implicit and only correct if we don't change the vector's length. Since we don't need to know the actual colour, we can omit all transparent pixels and store an inside pixel's position as it's value in the vector. This way we don't need to create a new object for each pixel of the shape (that would be quite expensive!).
var v:Vector.<uint> shapeBoxBitmap.getVector(shapeBoxBitmap.rect);
var pixelNum:int = v.length;
for(var i:uint = 0; i < pixelNum; i++) {
if( v[i] && 0xFF000000 == 0) { // transparent pixel, outside off shape
v.splice(i,1);
} else {
v[i] = i;
}
}
//get random point
var randomPixel:int = v[Math.floor(Math.random()*v.length)];
var point:Point = new Point(randomPixel%shapeBitmap.width,int(randomPixel/shapeBitmap.width));

Multiple google markers on the same place

i'm trying to use google maps with markers. i do not have any problems with the placement of markers in the map, but how can i get the markers to split like google earth when i have to markers in the same place? like this : Example
Thanks !
I didn't understand what you're trying to accomplish, but ...
Did you already check a marker clustering algorithm like this one or the google semi-official ?
//Here is my attempt... a Archimedes spiraling out of the markers:
// calc a spiraling out position based on marker count at that location
// this function is very tweeky
function spiral_coords(lat_long, i) {
i = (i == 1)? 0: i+1;
var r = i * 0.002;
// .8 is a fudge number to adjust to real appearance on the map
return [lat_long[0] + (r * .8 * Math.sin(.5 * (i + 2))), lat_long[1] + (r * Math.cos(.5 * (i + 2)))];
}
// this is from a fusion table query... but your source could be anything
// I take the coords and check against a hash count of them and calc out the spiral position
function data_handler(d) {
var map = $("#map")[0];
map.markers = [];
var rows = d.rows;
var fields = d.columns;
var index = {};
for (var i in fields) {
index[fields[i]] = i;
}
var location_count = {};
for (var i in rows) {
var row = rows[i];
var location = row[index["Location"]];
var lat_long = location.split(" ");
lat_long[0] = parseFloat(lat_long[0]);
lat_long[1] = parseFloat(lat_long[1]);
// here are the active ingredients
if(!(location in location_count)) {
location_count[location] = 0;
}
location_count[location]++;
lat_long = spiral_coords(lat_long, location_count[location]);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(lat_long[0], lat_long[1]),
map: map.map
});
}
}