Last active
January 9, 2020 16:12
-
-
Save lucien144/0001b8d4fd743c5cebf2e5a916ee22d3 to your computer and use it in GitHub Desktop.
Class to convert pixels & GPS coords and back for PHP and DART
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* https://stackoverflow.com/a/41527934/4026345 | |
*/ | |
import 'dart:math'; | |
import 'package:flutter/painting.dart'; | |
class MercatorProjection { | |
final DEFAULT_PROJECTION_WIDTH = 256; // ignore: non_constant_identifier_names | |
final DEFAULT_PROJECTION_HEIGHT = 256; // ignore: non_constant_identifier_names | |
final double centerLatitude; | |
final double centerLongitude; | |
final int areaWidthPx; | |
final int areaHeightPx; | |
final double areaScale; | |
final double mapScale; | |
int _projectionWidth; | |
int _projectionHeight; | |
double _pixelsPerLonDegree; | |
double _pixelsPerLonRadian; | |
double _projectionCenterPx; | |
double _projectionCenterPy; | |
int get projectionWidth => _projectionWidth; | |
MercatorProjection({this.centerLatitude, this.centerLongitude, this.areaWidthPx, this.areaHeightPx, this.areaScale, this.mapScale = 1}) { | |
// TODO stretch the projection to match to deformity at the center lat/lon? | |
this._projectionWidth = this.DEFAULT_PROJECTION_WIDTH; | |
this._projectionHeight = this.DEFAULT_PROJECTION_HEIGHT; | |
this._pixelsPerLonDegree = this._projectionWidth / 360 * this.mapScale; | |
this._pixelsPerLonRadian = this._projectionWidth / ((2 / this.mapScale) * pi); | |
var centerPoint = this.projectLocation(this.centerLatitude, this.centerLongitude); | |
this._projectionCenterPx = centerPoint.x * this.areaScale; | |
this._projectionCenterPy = centerPoint.y * this.areaScale; | |
} | |
Point<double> projectLocation(double latitude, double longitude) { | |
var x = this._projectionWidth / 2 + longitude * this._pixelsPerLonDegree; | |
var siny = sin(this.deg2rad(latitude)); | |
var y = this._projectionHeight / 2 + 0.5 * log((1 + siny) / (1 - siny)) * -this._pixelsPerLonRadian; | |
return Point<double>(x, y); | |
} | |
Offset getLocation(int px, int py) { | |
var x = this._projectionCenterPx + (px - this.areaWidthPx / 2); | |
var y = this._projectionCenterPy + (py - this.areaHeightPx / 2); | |
return this.projectPx(x / this.areaScale, y / this.areaScale); | |
} | |
Point<int> getPoint(double latitude, double longitude) { | |
var point = this.projectLocation(latitude, longitude); | |
var x = (point.x * this.areaScale - this._projectionCenterPx) + this.areaWidthPx / (2 / this.mapScale); | |
var y = (point.y * this.areaScale - this._projectionCenterPy) + this.areaHeightPx / (2 / this.mapScale); | |
return Point<int>(x.toInt(), y.toInt()); | |
} | |
Offset projectPx(double px, double py) { | |
var longitude = (px - this._projectionWidth / 2) / this._pixelsPerLonDegree; | |
var latitudeRadians = (py - this._projectionHeight / 2) / -this._pixelsPerLonRadian; | |
var latitude = this.rad2deg(2 * atan(exp(latitudeRadians)) - pi / 2); | |
return Offset(latitude, longitude); | |
} | |
double deg2rad(double deg) { | |
return (deg * pi) / 180; | |
} | |
double rad2deg(double rad) { | |
return (rad * 180) / pi; | |
} | |
int get projectionHeight => _projectionHeight; | |
double get pixelsPerLonDegree => _pixelsPerLonDegree; | |
double get pixelsPerLonRadian => _pixelsPerLonRadian; | |
double get projectionCenterPx => _projectionCenterPx; | |
double get projectionCenterPy => _projectionCenterPy; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php declare(strict_types = 1); | |
namespace Projection; | |
use CropCount\Model\Pixel; | |
use CropCount\Model\Point; | |
/** | |
* https://stackoverflow.com/a/41527934/4026345 | |
* | |
* Class MercatorProjection | |
* | |
* @package CropCount | |
*/ | |
class MercatorProjection | |
{ | |
private const DEFAULT_PROJECTION_WIDTH = 256; | |
private const DEFAULT_PROJECTION_HEIGHT = 256; | |
/** | |
* @var float | |
*/ | |
private $centerLatitude; | |
/** | |
* @var float | |
*/ | |
private $centerLongitude; | |
/** | |
* @var int | |
*/ | |
private $areaWidthPx; | |
/** | |
* @var int | |
*/ | |
private $areaHeightPx; | |
/** | |
* the scale that we would need for the a projection to fit the given area into a world view (1 = global, expect it to be > 1) | |
* | |
* @var float | |
*/ | |
private $areaScale; | |
/** | |
* @var int | |
*/ | |
private $mapScale; | |
/** | |
* @var int | |
*/ | |
private $projectionWidth; | |
/** | |
* @var int | |
*/ | |
private $projectionHeight; | |
/** | |
* @var float|int | |
*/ | |
private $pixelsPerLonDegree; | |
/** | |
* @var float|int | |
*/ | |
private $pixelsPerLonRadian; | |
/** | |
* @var float|int | |
*/ | |
private $projectionCenterPx; | |
/** | |
* @var float|int | |
*/ | |
private $projectionCenterPy; | |
public function __construct(float $centerLatitude, float $centerLongitude, int $areaWidthPx, int $areaHeightPx, float $areaScale, float $mapScale = 1) | |
{ | |
$this->centerLatitude = $centerLatitude; | |
$this->centerLongitude = $centerLongitude; | |
$this->areaWidthPx = $areaWidthPx; | |
$this->areaHeightPx = $areaHeightPx; | |
$this->areaScale = $areaScale; | |
$this->mapScale = $mapScale; | |
// TODO stretch the projection to match to deformity at the center lat/lon? | |
$this->projectionWidth = self::DEFAULT_PROJECTION_WIDTH; | |
$this->projectionHeight = self::DEFAULT_PROJECTION_HEIGHT; | |
$this->pixelsPerLonDegree = $this->projectionWidth / 360 * $this->mapScale; | |
$this->pixelsPerLonRadian = $this->projectionWidth / ((2 / $this->mapScale) * \M_PI); | |
$centerPoint = $this->projectLocation($this->centerLatitude, $this->centerLongitude); | |
$this->projectionCenterPx = $centerPoint->x * $this->areaScale; | |
$this->projectionCenterPy = $centerPoint->y * $this->areaScale; | |
} | |
public function projectLocation(float $latitude, float $longitude): object | |
{ | |
$px = $this->projectionWidth / 2 + $longitude * $this->pixelsPerLonDegree; | |
$siny = sin($this->deg2rad($latitude)); | |
$py = $this->projectionHeight / 2 + 0.5 * log((1 + $siny) / (1 - $siny)) * -$this->pixelsPerLonRadian; | |
return (object) ['x' => $px, 'y' => $py]; | |
} | |
private function deg2rad(float $deg): float | |
{ | |
return ($deg * \M_PI) / 180; | |
} | |
public function getLocation(int $px, int $py): object | |
{ | |
$x = $this->projectionCenterPx + ($px - $this->areaWidthPx / 2); | |
$y = $this->projectionCenterPy + ($py - $this->areaHeightPx / 2); | |
return $this->projectPx($x / $this->areaScale, $y / $this->areaScale); | |
} | |
public function getPoint(float $latitude, float $longitude): object | |
{ | |
$point = $this->projectLocation($latitude, $longitude); | |
$x = ($point->x * $this->areaScale - $this->projectionCenterPx) + $this->areaWidthPx / (2 / $this->mapScale); | |
$y = ($point->y * $this->areaScale - $this->projectionCenterPy) + $this->areaHeightPx / (2 / $this->mapScale); | |
return new Pixel((int) round($x), (int) round($y)); | |
} | |
public function projectPx(float $px, float $py): object | |
{ | |
$longitude = ($px - $this->projectionWidth / 2) / $this->pixelsPerLonDegree; | |
$latitudeRadians = ($py - $this->projectionHeight / 2) / -$this->pixelsPerLonRadian; | |
$latitude = $this->rad2deg(2 * atan(exp($latitudeRadians)) - \M_PI / 2); | |
return new Point($latitude,$longitude); | |
} | |
private function rad2deg(float $rad): float | |
{ | |
return ($rad * 180) / \M_PI; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment