Point in Polygon algorithm giving wrong results sometimes [closed] - php
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I saw on StackOverflow a "point in polygon" raytracing algorithm that I implemented in my PHP Code. Most of the time, it works well, but in some complicated cases, with complex polygons and vicious points, it fails and it says that point in not in polygon when it is.
For example:
You will find here my Polygon and Point classes: pointInPolygon method is in Polygon class. At the end of the file, there are two points that are supposed to lie inside the given polygon (True on Google Earth). The second one works well, but the first one is buggy :( .
You can easily check the polygon on Google Earth using this KML file.
Have been there :-) I also travelled through Stackoverflow's PiP-suggestions, including your reference and this thread. Unfortunately, none of the suggestions (at least those I tried) were flawless and sufficient for a real-life scenario: like users plotting complex polygons on a Google map in freehand, "vicious" right vs left issues, negative numbers and so on.
The PiP-algorithm must work in all cases, even if the polygon consists of hundreds of thousands of points (like a county-border, nature park and so on) - no matter how "crazy" the polygon is.
So I ended up building a new algorithm, based on some source from an astronomy-app:
//Point class, storage of lat/long-pairs
class Point {
public $lat;
public $long;
function Point($lat, $long) {
$this->lat = $lat;
$this->long = $long;
}
}
//the Point in Polygon function
function pointInPolygon($p, $polygon) {
//if you operates with (hundred)thousands of points
set_time_limit(60);
$c = 0;
$p1 = $polygon[0];
$n = count($polygon);
for ($i=1; $i<=$n; $i++) {
$p2 = $polygon[$i % $n];
if ($p->long > min($p1->long, $p2->long)
&& $p->long <= max($p1->long, $p2->long)
&& $p->lat <= max($p1->lat, $p2->lat)
&& $p1->long != $p2->long) {
$xinters = ($p->long - $p1->long) * ($p2->lat - $p1->lat) / ($p2->long - $p1->long) + $p1->lat;
if ($p1->lat == $p2->lat || $p->lat <= $xinters) {
$c++;
}
}
$p1 = $p2;
}
// if the number of edges we passed through is even, then it's not in the poly.
return $c%2!=0;
}
Illustrative test :
$polygon = array(
new Point(1,1),
new Point(1,4),
new Point(4,4),
new Point(4,1)
);
function test($lat, $long) {
global $polygon;
$ll=$lat.','.$long;
echo (pointInPolygon(new Point($lat,$long), $polygon)) ? $ll .' is inside polygon<br>' : $ll.' is outside<br>';
}
test(2, 2);
test(1, 1);
test(1.5333, 2.3434);
test(400, -100);
test(1.01, 1.01);
Outputs :
2,2 is inside polygon
1,1 is outside
1.5333,2.3434 is inside polygon
400,-100 is outside
1.01,1.01 is inside polygon
It is now more than a year since I switched to the above algorithm on several sites. Unlike the "SO-algorithms" there have not been any complaints so far. See it in action here (national mycological database, sorry for the Danish). You can plot a polygon, or select a "kommune" (a county) - ultimately compare a polygon with thousands of points to thousands of records).
Update
Note, this algorithm is targeting geodata / lat,lngs which can be very precise (n'th decimal), therefore considering "in polygon" as inside polygon - not on border of polygon. 1,1 is considered outside, since it is on the border. 1.0000000001,1.01 is not.
Related
Arithmetic on large numbers
I've been thinking about ways to program for exteme distances, in the game Hellion for example, orbits could be near to scale in the ranges of millions of kilometers. However there was a common glitch where movement would be very choppy the further you were from the object you were orbiting. I might be wrong in my speculation to why that was, but my best guess was that it was down to loss of precision at that distance. As a little exercise I've been thinking about ways to solve that problem and what I currently have is a a pretty basic unit staged distance system. class Distance { public const MAX_AU = 63018.867924528; public const MAX_MM = 149598000000000; private $ly = 0; private $au = 0; private $mm = 0; public function add(Distance $add): Distance { $distance = new Distance(); $distance->mm = $this->mm + $add->mm; if ($distance->mm > self::MAX_MM) { $distance->mm-= self::self::MAX_MM; $distance->au++; } $distance->au+= $this->au + $add->au; if ($distance->au > self::MAX_AU) { $distance->au-= self::self::MAX_AU; $distance->ly++; } $distance->ly+= $this->ly + $add->ly; return $distance; } } I put in the addition method, which is written as though by hand. I didn't want to use arbitary precision because it would have been too extreme for calculating the smaller distances a player would normally interact with. My question is; is this how something like this is normally done? and if not, could someone please explain what is wrong with this (inefficient for example), and how it could be done better? Thanks PS. I am aware that in the context of a game, this is normally handled with sub-grids, but to simulate how objects in orbit would drift apart, thats what this is for.
You can use the BCMath functions. BCMath supports numbers of any size and precision. Example: $mm = "149598000000000"; $au = "63018.867924528"; $sum = bcadd($mm,$au,10); echo $sum; //149598000063018.8679245280
Display error using google maps tile server and Google Maps utility in PHP
I have a question regarding Map tile server and coordinates conversion in Google Maps using the Google Maps Utility library. My tile server accesses a database with thousands of gps coordinates (lat,lng), and for every (lat,lng) point, checks if the point is inside the geographical bounds of the tile; if it does, coordinates conversion (WGS84 -> Mercator -> X,Y offset inside the tile) and paints the corresponding pixel inside the tile, using the GoogleMapsUtility Library. In terms of code, I do the following: $point = GoogleMapUtility::getOffsetPixelCoords((float)$row['lat'], (float)$row['lng'], $zoom, $X, $Y); which calls the getOffsetPixelCoords function (and in turn the functions below) from the library: public static function getOffsetPixelCoords($lat,$lng,$zoom, $X, $Y) { $pixelCoords = GoogleMapUtility::getPixelCoords($lat, $lng, $zoom); return new Point( $pixelCoords->x - $X * GoogleMapUtility::TILE_SIZE, $pixelCoords->y - $Y * GoogleMapUtility::TILE_SIZE ); } public static function getPixelCoords($lat, $lng, $zoom) { $normalised = GoogleMapUtility::_toNormalisedMercatorCoords(GoogleMapUtility::_toMercatorCoords($lat, $lng)); $scale = (1 << ($zoom)) * GoogleMapUtility::TILE_SIZE; return new Point( (int)($normalised->x * $scale), (int)($normalised->y * $scale) ); } private static function _toNormalisedMercatorCoords($point) { $point->x += 0.5; $point->y = abs($point->y-0.5); return $point; } Ok, now the results. For a zoom level<13 it works great, below is an example of a tile in Zoom level 11: Image1 However, for a tile in zoom level >13, the following happens: Image2 Which is so strange... the pixels seem to be perfectly aligned ? At first I thought it is a decimal resolution problem, but the resolution of the data is quite good (stored as double in a mysql database, for example, 35.6185989379883, 139.731994628906, and in php floats and doubles are the same thing...) Could someone help me on how to fix this problem? Thanks in advance...
Why do you use type casting on the result of the database query? In the example of the book googlemapsutility it's not there?
Calculate circumference values
I have a rectangular map, stored as multidimensional array (ie $map[row][col]) and I have to track down which squares are seen by a player, placed anywhere on this map. Player visibility is circular with unknown radius (but given at run-time) and I only need integer solutions. I know that circumference formula is x^2 + y^2 <= r^2 but how can I store everything? I need these values since then I can "reveal" map squares. The best would be a multidimesional array (ie __$sol[x][y]__). This is a piece of code that I'm using. It's not corrected since it assumes that vision is a square and not a circle. Calculating the square $this->vision_offsets_2 = array(); //visibility given as r^2 $mx = (int)(sqrt($this->viewradius2)); $mxArr = range($mx * -1, $mx + 1); foreach ($mxArr as $d_row) { foreach ($mxArr as $d_col) { $this->vision_offsets_2[] = array($d_row, $d_col); } } This is how I apply that foreach($player as $bot) { foreach($visibility as $offset) { $vision_row = $offset[0] + $bot[0]; $vision_col = $offset[1] + $bot[1]; if(isset($map[$vision_row][$vision_col])) { if( $map[$vision_row][$vision_col] == UNSEEN) { $map[$vision_row][$vision_col] = LAND; } } } } Here you can find the bot view: as you can see is a non perfect circle. How can I track it? By the way, in this example radius^2 is 55, the orange circle is the player, brown squares are visible ones.
Structure You're already referencing terrain in a grid. Store terrain objects in those grid values. Apply attributes to those objects. Check with something like $map[$x][$y]->isVisible($player); You'll need some methods in there for setting vision and tests for checking the user that is passed against a list of users who can see it. While you're at it, setup other related methods in those objects (I see references to land... isLand() and isWater() perhaps?). You can even have vision cascade within objects such that you only need to move the position of a user and the object takes care of triggering off all the code to set nearby plots of land to visible. Math We are given circumference. double diameter = circumference / 3.14159 double radius = diameter / 2 //Normally done in one step / variable Now we must know the distance between two points to compare it. Let's use map[4][7] and map[3][9]. int x0 = 4; int y0 = 7; int x1 = 3; int y1 = 9; double distance = Math.sqrt( Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2) ); System.out.println(distance); //2.23606797749979 Test distance > radius. Testing each square Put the above in a method: visibleFrom(Square target) radius should be a globally accessible static variable when comparing. Your Square object should be able to hand over its coordinates. target.getX() target.getY() Some optimizations can be had Only checking things for circular distance when they're in the square. Not checking anything for circular distance when purely along the x or y axis. Figuring out the largest square that fits inside the circle and not checking boxes in that range for circular distance. Remember that premature optimization and over optimization are pitfalls.
A function like this would tell you if a map square is visible (using the distance of the centers of the squares as a metric; if you want to define visibility in another manner, which you probably would, things get much more complicated): function is_visible($mapX, $mapX, $playerX, $playerY, $r) { return sqrt(pow($mapX - $playerX, 2) + pow($mapY - $playerY, 2)) <= $r; } You probably don't really need to store these values since you can easily calculate them on demand.
I think that Bresenham's circle drawing algorithm is what you're looking for.
I don't know exactly what you want, but here's some things that should help you along. As a warning these are untested, but the logic is sound. //You mentioned circumference, this will find out the circumference but I don't //think you actually need it. $circumference_length = 2 * $visibility_range * 3.1415; //Plug in the player and target coordinates and how far you can see, this will //tell you if the player can see it. This can be optimized using your object //and player Objects. function canSee($player_x, $player_y, $vision_length, $target_x, $target_y){ $difference_x = $target_x - $player_x; $difference_y = $target_y - $player_y; $distance = sqrt((pow($difference_x,2) + pow($difference_y, 2)); if($vision < $distance){ return false; } else { return true; } } Edit: In response to your clarification, you can use the above function to figure out if you should show the terrain objects or not. foreach($player as $bot) { foreach($terrain_thing as $terrain) { //ASSUMING THAT [0] IS ALWAYS X AND [1] IS ALWAYS y, set a third variable //to indicate visibility $terrain["is_visible"] = canSee($bot[0], $bot[1], $visibility_range, $terrain[0], $terrain[1]) } }
Converting Python Code to PHP [closed]
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers. We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations. Closed 1 year ago. Improve this question Is there a software converter out there that can automatically convert this python code to PHP? #!/usr/bin/python import math def calcNumEntropyBits(s): if len(s) <= 0: return 0.0 symCount = {} for c in s: if c not in symCount: symCount[c] = 1 else: symCount[c] += 1 entropy = 0.0 for c,n in symCount.iteritems(): prob = n / float(len(s)) entropy += prob * (math.log(prob)/math.log(2)) if entropy >= 0.0: return 0.0 else: return -(entropy*len(s)) def testEntropy(s): print "Bits of entropy in '%s' is %.2f" % (s, calcNumEntropyBits(s)) testEntropy('hello world') testEntropy('bubba dubba') testEntropy('aaaaaaaaaaa') testEntropy('aaaaabaaaaa') testEntropy('abcdefghijk')
I'm not aware of any Python-to-PHP converter in the wild, but it should be a trivial task to port and the similarities are quite easy to spot: function calcNumEntropyBits($s) { if (strlen($s) <= 0) return 0.0; $symCount = array(); foreach (str_split($s) as $c) { if (!in_array($c,$symCount)) $symCount[$c] = 1; else $symCount[$c] ++; } $entropy = 0.0; foreach ($symCount as $c=>$n) { $prob = $n / (float)strlen($s); $entropy += $prob * log($prob)/log(2); } if ($entropy >= 0.0) return 0.0; else return -($entropy*strlen($s)); } function testEntropy($s): printf("Bits of entropy in '%s' is %.2f",$s,calcNumEntropyBits($s)); testEntropy('hello world'); testEntropy('bubba dubba'); testEntropy('aaaaaaaaaaa'); testEntropy('aaaaabaaaaa'); testEntropy('abcdefghijk'); The last few lines in the first function could have also been written as a standard PHP ternary expression: return ($entropy >= 0.0)? 0.0: -($entropy*strlen($s));
I created a python-to-php converter called py2php. It can auto-translate the basic logic and then you will need to tweak library calls, etc. Still experimental. Here is auto-generated PHP from the python provided by the OP. <?php require_once('py2phplib.php'); require_once( 'math.php'); function calcNumEntropyBits($s) { if ((count($s) <= 0)) { return 0.0; } $symCount = array(); foreach( $s as $c ) { if (!$symCount.__contains__($c)) { $symCount[$c] = 1; } else { $symCount[$c] += 1; } } $entropy = 0.0; foreach( $symCount->iteritems() as $temp_c ) { $prob = ($n / float(count($s))); $entropy += ($prob * (math::log($prob) / math::log(2))); } if (($entropy >= 0.0)) { return 0.0; } else { return -($entropy * count($s)); } } function testEntropy($s) { pyjslib_printFunc(sprintf('Bits of entropy in \'%s\' is %.2f', new pyjslib_Tuple([$s, calcNumEntropyBits($s)]))); } testEntropy('hello world'); testEntropy('bubba dubba'); testEntropy('aaaaaaaaaaa'); testEntropy('aaaaabaaaaa'); testEntropy('abcdefghijk'); It would not run correctly due to the math import and __contains__, but those would be easy enough to fix by hand.
I am about 1/2 way done making a PHP interpreter in Python and I can tell you flat out that there are literally dozens of major edge cases that play out to thousands of possibilities that would make it almost impossible to port Python to PHP. Python has a much more robust grammar then PHP while further foward in the language, Python's stdlib is probably one of the most advanced in comparison to any other language in it's class. My recommendation is to take your question one step further back, to why do you need a set of Python based logic in PHP. Alternatives to attempting to port/translate your code could include subprocessing from PHP to Python, using Gearman to have Python do work in the backend while PHP handles view logic, or a much more involved solution would be to implement a service bus or message queue between a PHP application and Python services. PS. Apologies for any readability issues, finishing a 2 day sprint just now.
No such tool exists, you'll have to port the code yourself
I changed the library py2php from https://github.com/dan-da/py2php and forked it into a new repository at https://github.com/bunkahle/py2php You now can also use the python math library which is translated to PHP code. Still you have to do adaptations to your code in order to get it to work.
I wondered myself that same question, and I found this PyToPhp.py file in the GitHubGist site. It is simple, and seem an start point for the begining. I'm going to take a look to it!!!
How to find the centre of a grid of lines on google maps
I'm struggling with a problem with some GIS information that I am trying to put into a KML file to be used by google maps and google Earth. I have SQL database containing a number of surveys, which are stored in lines. When all the lines are drawn on the map, they create a grid. What I am trying to do is work out the centre of these 'grids' so that I can put a placemarker reference into the kml to show all the grid locations on a map. lines is stored in the database like this: 118.718318,-19.015803,0 118.722449,-19.016919,0 118.736223,-19.020637,0 118.749936,-19.024023,0 118.763897,-19.027722,0 118.777705,-19.031277,0 118.791416,-19.034826,0 118.805276,-19.038367,0 118.818862,-19.041962,0 118.832862,-19.045582,0 118.846133,-19.049563,0 118.859801,-19.053851,0 118.873322,-19.058145,0 118.887022,-19.062349,0 118.900595,-19.066594,0 118.914066,-19.070839,0 118.927885,-19.075151,0 118.941468,-19.079354,0 118.955064,-19.083658,0 118.968766,-19.087896,0 118.982247,-19.092054,0 118.995795,-19.096324,0 119.009192,-19.100448,0 119.022805,-19.104787,0 119.036414,-19.10893,0 119.049625,-19.113166,0 119.063155,-19.11738,0 119.076626,-19.121563,0 119.090079,-19.125738,0 119.103679,-19.129968,0 119.117009,-19.134168,0 119.130637,-19.138418,0 119.144134,-19.142613,0 119.157749,-19.146767,0 119.171105,-19.151058,0 119.184722,-19.155252,0 119.19844,-19.159399,0 119.211992,-19.163737,0 119.225362,-19.167925,0 119.239109,-19.17218,0 119.252552,-19.176239,0 119.265975,-19.180483,0 119.279718,-19.184901,0 119.2931,-19.189118,0 119.306798,-19.193267,0 119.320561,-19.197631,0 119.33389,-19.201898,0 119.34772,-19.206063,0 119.361322,-19.210282,0 119.374522,-19.214505,0 119.38825,-19.218753,0 119.401948,-19.22299,0 119.415609,-19.227171,0 119.42913,-19.231407,0 119.44269,-19.235604,0 119.456221,-19.240254,0 119.469772,-19.244165,0 119.483159,-19.248341,0 119.496911,-19.252483,0 119.510569,-19.256818,0 119.524034,-19.261068,0 119.537822,-19.265332,0 119.551409,-19.269529,0 119.564744,-19.273764,0 119.578357,-19.277874,0 119.591935,-19.282171,0 119.605472,-19.286424,0 119.619009,-19.290593,0 119.632777,-19.294862,0 119.646394,-19.299146,0 119.660107,-19.303469,0 119.673608,-19.307602,0 119.6872,-19.31182,0 119.700838,-19.31605,0 119.714555,-19.320301,0 119.728202,-19.324615,0 119.741511,-19.328794,0 119.755299,-19.333098,0 119.768776,-19.337206,0 119.782216,-19.341528,0 119.796141,-19.345829,0 119.809691,-19.349879,0 119.822889,-19.354112,0 119.836822,-19.358414,0 119.850077,-19.362618,0 119.864069,-19.366916,0 119.8773,-19.371092,0 119.891263,-19.37536,0 119.904612,-19.379556,0 119.918522,-19.38394,0 119.932101,-19.388108,0 119.945577,-19.392184,0 119.959304,-19.396544,0 119.973042,-19.400809,0 119.986433,-19.405015,0 119.999196,-19.408981,0 120.011959,-19.412946,0 120.014512,-19.41374,0 118.990393,-19.523933,0 118.990833,-19.522296,0 118.994368,-19.509069,0 118.997911,-19.49589,0 119.001249,-19.48258,0 119.004939,-19.469658,0 119.008437,-19.456658,0 119.01183,-19.443361,0 119.015332,-19.430439,0 119.01869,-19.417491,0 119.022303,-19.40421,0 119.025853,-19.391381,0 119.029387,-19.377966,0 119.032821,-19.365006,0 119.036247,-19.352047,0 119.039935,-19.338701,0 119.043376,-19.325781,0 119.046824,-19.312713,0 119.050223,-19.299634,0 119.053822,-19.286711,0 119.05736,-19.273441,0 119.060722,-19.260467,0 119.064357,-19.247382,0 119.067863,-19.234069,0 119.071361,-19.221168,0 119.07486,-19.208268,0 119.078303,-19.19503,0 119.081721,-19.181915,0 119.085296,-19.169016,0 119.088851,-19.155937,0 119.092245,-19.142828,0 119.095887,-19.12944,0 119.099337,-19.116291,0 119.102821,-19.103421,0 119.106333,-19.090384,0 119.109885,-19.077356,0 119.113395,-19.063976,0 119.116866,-19.050973,0 119.120467,-19.037886,0 119.123751,-19.024888,0 119.127385,-19.011622,0 119.130775,-18.998856,0 119.134503,-18.98547,0 119.13873,-18.9728,0 119.14289,-18.959666,0 119.146076,-18.946541,0 119.149065,-18.933693,0 119.152281,-18.920522,0 119.155414,-18.907033,0 119.158442,-18.894058,0 119.161421,-18.88097,0 119.164632,-18.867484,0 119.167668,-18.854501,0 119.170744,-18.841401,0 119.173939,-18.827999,0 119.17703,-18.815028,0 119.17984,-18.802218,0 119.182902,-18.789334,0 119.186088,-18.776136,0 119.189031,-18.762862,0 119.192179,-18.749824,0 119.19539,-18.7365,0 119.198255,-18.723279,0 119.201476,-18.710134,0 119.204345,-18.69705,0 119.20746,-18.683768,0 119.210555,-18.670607,0 119.213641,-18.657632,0 119.216727,-18.644301,0 119.21982,-18.630956,0 119.223094,-18.617243,0 119.22625,-18.603885,0 119.229368,-18.590534,0 119.232494,-18.577364,0 119.235477,-18.564287,0 119.238496,-18.550953,0 119.241392,-18.53789,0 119.244609,-18.524881,0 119.247551,-18.512017,0 119.250532,-18.498916,0 119.253729,-18.485859,0 119.256428,-18.473845,0 119.259288,-18.461645,0 119.262064,-18.4491,0 119.265024,-18.437048,0 119.267877,-18.424992,0 119.270457,-18.4136,0 119.273429,-18.401305,0 119.276346,-18.388484,0 119.279506,-18.375299,0 119.282307,-18.362197,0 119.285494,-18.34918,0 119.288695,-18.336115,0 119.291008,-18.323772,0 119.294026,-18.310635,0 119.297332,-18.297714,0 119.300499,-18.284553,0 119.303442,-18.271193,0 119.307081,-18.258278,0 119.309945,-18.245139,0 119.313121,-18.232137,0 119.31642,-18.218993,0 119.319499,-18.205722,0 119.322801,-18.192774,0 119.325986,-18.179558,0 119.329173,-18.166332,0 119.33236,-18.15312,0 119.335558,-18.140071,0 119.338761,-18.126696,0 119.342007,-18.113502,0 119.345238,-18.100349,0 119.34808,-18.088343,0 119.350259,-18.079138,0 119.912412,-18.177179,0 119.910223,-18.183223,0 119.905619,-18.195973,0 119.901171,-18.208645,0 119.896641,-18.221452,0 119.89198,-18.234323,0 119.887547,-18.246912,0 119.882922,-18.259925,0 119.878421,-18.272522,0 119.873909,-18.285223,0 119.869264,-18.298194,0 119.864746,-18.310939,0 119.860203,-18.323628,0 119.855692,-18.336434,0 119.850997,-18.349262,0 119.846561,-18.362014,0 119.841916,-18.374866,0 119.837406,-18.387565,0 119.832778,-18.400657,0 119.828259,-18.413072,0 119.82364,-18.426273,0 119.819088,-18.438992,0 119.814546,-18.451696,0 119.810103,-18.464425,0 119.80548,-18.477041,0 119.800935,-18.48989,0 119.796359,-18.502741,0 119.791812,-18.515489,0 119.787314,-18.528536,0 119.782724,-18.540965,0 119.778079,-18.553994,0 119.773558,-18.56663,0 119.768931,-18.57955,0 119.76446,-18.592177,0 119.759793,-18.605255,0 119.755393,-18.617736,0 119.750648,-18.630757,0 119.746479,-18.643371,0 119.741779,-18.656176,0 119.737071,-18.669135,0 119.732572,-18.681798,0 119.727954,-18.694669,0 119.723453,-18.707473,0 119.718855,-18.720259,0 119.714169,-18.733198,0 119.70946,-18.746355,0 119.704998,-18.759005,0 119.700581,-18.771793,0 119.696226,-18.784893,0 119.691577,-18.797373,0 119.68662,-18.810064,0 119.682182,-18.822772,0 119.677711,-18.835621,0 119.672955,-18.848475,0 119.668536,-18.861252,0 119.663856,-18.873901,0 119.659412,-18.88663,0 119.654815,-18.899592,0 119.650185,-18.912338,0 119.645586,-18.925118,0 119.640925,-18.937923,0 119.636617,-18.950753,0 119.631874,-18.963446,0 119.627376,-18.976275,0 119.622845,-18.98893,0 119.618175,-19.001942,0 119.613727,-19.014668,0 119.608878,-19.027398,0 119.60445,-19.039954,0 119.599954,-19.053249,0 119.595066,-19.066008,0 119.590562,-19.078866,0 119.586062,-19.091703,0 119.58155,-19.104199,0 119.576948,-19.116924,0 119.572431,-19.129926,0 119.567449,-19.142699,0 119.563227,-19.155474,0 119.558555,-19.168309,0 119.553956,-19.181053,0 119.549545,-19.193649,0 119.544854,-19.20646,0 119.540133,-19.21929,0 119.535606,-19.232162,0 119.53108,-19.245054,0 119.526509,-19.257772,0 119.522079,-19.270427,0 119.517419,-19.283013,0 119.512755,-19.296126,0 119.508159,-19.309031,0 119.503557,-19.321821,0 119.498966,-19.334394,0 119.494487,-19.347232,0 119.489815,-19.359907,0 119.485201,-19.372962,0 119.48067,-19.385811,0 119.476151,-19.398326,0 119.471526,-19.411138,0 119.466856,-19.424266,0 119.462284,-19.436788,0 119.457752,-19.449628,0 119.452975,-19.462718,0 119.448612,-19.47499,0 119.444249,-19.48726,0 119.439886,-19.499531,0 119.437704,-19.505666,0 Each point is where a mesasurement took place. Is there a PHP library or a formula that can be used to work this out without being too intensive? Thanks
It depends very much on how you define "center". One somewhat sophisticated approach you might like to try: Extract a list of points from the lines Find the convex hull of the points: Find the centroid of the convex hull (source: algorithmic-solutions.info) The convex hull is simply described as the simplest polygon that passes through some of the points, forming an envelop that encloses all of the points within the polygon. wikipedia has a more rigorous description. The centroid provides a simple way for finding the "center of gravity" of a polygon. A wikipedia and a nice tutorial provide more information.
Alternatively, a very simple implementation might find the minimum bounding rectangle of the lines, and then find the geometric center of the box. The following code assumes the lines you posted above are represented as strings, and are in an array. I'm no php performance expert, but this algorithm should execute in O(n) asymptotic time: <?php $line_strs = array( "118.718318,-19.015803,0 118.722449,-19.016919,0 118.736223,-19.020637,0 118.749936,-19.024023,0 118.763897,-19.027722,0 118.777705,-19.031277,0", "118.791416,-19.034826,0 118.805276,-19.038367,0 118.818862,-19.041962,0 118.832862,-19.045582,0 118.846133,-19.049563,0 118.859801,-19.053851,0", [SNIP majority of lines...] "119.448612,-19.47499,0 119.444249,-19.48726,0 119.439886,-19.499531,0 119.437704,-19.505666,0"); $xs = array(); $ys = array(); foreach ($line_strs as $line){ $points = explode(' ', $line); foreach ($points as $pt){ $xyz = explode(',', $pt); $xs[] = (float)$xyz[0]; $ys[] = (float)$xyz[1]; } } $x_mean = (max($xs) + min($xs)) / 2; $y_mean = (max($ys) + min($ys)) / 2; echo "$x_mean,$y_mean\n"; ?> This outputs 119.366415,-18.8015355.
centroids ? If I have time, I'll edit this answer to include a python implementation
Another very simple answer finds the mean location of all vertices. It favours areas that are densely populated by points. This returns a slightly different answer to the bounding box method. You might want to try them box to see which provides a "better" result for your data. <?php $line_strs = array( "118.718318,-19.015803,0 118.722449,-19.016919,0 118.736223,-19.020637,0 118.749936,-19.024023,0 118.763897,-19.027722,0 118.777705,-19.031277,0", "118.791416,-19.034826,0 118.805276,-19.038367,0 118.818862,-19.041962,0 118.832862,-19.045582,0 118.846133,-19.049563,0 118.859801,-19.053851,0", [SNIP majority of lines...] "119.448612,-19.47499,0 119.444249,-19.48726,0 119.439886,-19.499531,0 119.437704,-19.505666,0"); $x_sum = 0.0; $y_sum = 0.0; $n = 0; foreach ($line_strs as $line){ $points = explode(' ', $line); foreach ($points as $pt){ $xyz = explode(',', $pt); $x_sum += (float)$xyz[0]; $y_sum += (float)$xyz[1]; $n++; } } $x_mean = $x_sum / $n; $y_mean = $y_sum / $n; echo "$x_mean,$y_mean\n"; ?> This outputs 119.402034114,-18.9427670536.