2

I am passing a center point (latitude, longitude) with a radius to this function to get the circumference points (latitude, longitude) so that I make a geometry from them and store it in MYSQL for me to do ST_Contains function later on.

This is getting me an oval shape instead of a circle when drawing them on Google Maps.

public static function convert($center, $radius, $numberOfSegments = 360)
{
    $n = $numberOfSegments;
    $flatCoordinates = [];
    $coordinates = [];
    for ($i = 0; $i < $n; $i++) {
        $flatCoordinates = array_merge($flatCoordinates, static::offset($center, $radius, 2 * pi() * $i / $n));
    }
    $flatCoordinates[] = $flatCoordinates[0];
    $flatCoordinates[] = $flatCoordinates[1];
    for ($i = 0, $j = 0; $j < count($flatCoordinates); $j += 2) {
        $coordinates[$i++] = array_slice($flatCoordinates, $j, 2);
    }

    return [
        'type' => 'Polygon',
        'coordinates' => [array_reverse($coordinates)]
    ];
}

public static function toRadians($angleInDegrees = null)
{
    return $angleInDegrees * pi() / 180;
}

public static function toDegrees($angleInRadians = null)
{
    return $angleInRadians * 180 / pi();
}

public static function offset($c1, $distance, $bearing)
{
    $lat1 = static::toRadians($c1[1]);
    $lon1 = static::toRadians($c1[0]);
    $dByR = $distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
    $lat = asin(
        sin($lat1) * cos($dByR) +
            cos($lat1) * sin($dByR) * cos($bearing)
    );
    $lon = $lon1 + atan2(
        sin($bearing) * sin($dByR) * cos($lat1),
        cos($dByR) - sin($lat1) * sin($lat)
    );
    return [static::toDegrees($lon), static::toDegrees($lat)];
}

I do not really need it to be as accurate in terms of the earth curvature with taking into consideration WGS84, as the radius is quite small (25m-150m).

miken32
  • 42,008
  • 16
  • 111
  • 154
rfpdl
  • 956
  • 1
  • 11
  • 35
  • Are you sure this isn't a projection issue with Google maps? PS: https://php.net/deg2rad https://php.net/rad2deg Also `M_PI` constant returns same as `pi()` function. – miken32 May 01 '19 at 15:21
  • Actually, I just tried this code and it's got more problems than just drawing ovals. Running `convert([49, -122], 5000);` I get coordinates in Newfoundland (58º W) instead of BC (122ºW) – miken32 May 01 '19 at 22:00
  • @miken32 I am viewing the polygon in mysql workbench and the image for the polygon is oval http://prntscr.com/njd47v – rfpdl May 02 '19 at 06:08

1 Answers1

4

I'm guessing by the variable names that you got your function from this question but you forgot one line of the code, specifically the line that was causing that person problems.

In addition, your array code in the first function was quite more verbose than it needed to be. I condensed it considerably here, and used built-in PHP functions for the radians/degrees conversion:

<?php
function convert($center, $radius, $numberOfSegments = 360)
{
    $n = $numberOfSegments;
    $flatCoordinates = [];
    $coordinates = [];
    for ($i = 0; $i < $n; $i++) {
        $bearing = 2 * M_PI * $i / $n;
        $flatCoordinates[] = offset($center, $radius, $bearing);
    }
    $flatCoordinates[] = $flatCoordinates[0];

    return [
        'type' => 'Polygon',
        'coordinates' => [$flatCoordinates]
    ];
}

function offset($c1, $distance, $bearing) {
    $lat1 = deg2rad($c1[0]);
    $lon1 = deg2rad($c1[1]);
    $dByR = $distance/6378137; // convert dist to angular distance in radians

    $lat = asin(
        sin($lat1) * cos($dByR) + 
            cos($lat1) * sin($dByR) * cos($bearing)
    );
    $lon = $lon1 + atan2(
        sin($bearing) * sin($dByR) * cos($lat1),
        cos($dByR) - sin($lat1) * sin($lat)
    );
    $lon = fmod(
        $lon + 3 * M_PI,
        2 * M_PI
    ) - M_PI;
    return [rad2deg($lon), rad2deg($lat)];
}

$result = convert([49, -122], 5000, 20);
foreach ($result['coordinates'][0] as $v) echo "$v[1]\t$v[0]\n";

Output:

49.044915764206 -122
49.042715494198 -121.97882560793
49.036330613043 -121.9597290805
49.026387559463 -121.94458290688
49.01386140937  -121.93486968973
48.999979747353 -121.93153702633
48.986101953242 -121.93490597976
48.973585929464 -121.94464162541
48.963655392709 -121.95978779908
48.957280637886 -121.97886189803
48.955084235794 -122
48.957280637886 -122.02113810197
48.963655392709 -122.04021220092
48.973585929464 -122.05535837459
48.986101953242 -122.06509402024
48.999979747353 -122.06846297367
49.01386140937  -122.06513031027
49.026387559463 -122.05541709312
49.036330613043 -122.0402709195
49.042715494198 -122.02117439207
49.044915764206 -122

I used this site for testing and it drew the circle as expected.

miken32
  • 42,008
  • 16
  • 111
  • 154
  • I tried it, it is better than my output, but still not circle (http://prntscr.com/njd4qt) – rfpdl May 02 '19 at 06:10
  • When I draw it on google maps, it looks like a circle though http://prntscr.com/njd6uw – rfpdl May 02 '19 at 06:17