laravie/geotools

支持PHP 7+的地理相关工具库

v1.2.0 2021-05-30 14:40 UTC

This package is auto-updated.

Last update: 2024-09-05 08:13:54 UTC


README

Geotools 是一个PHP地理相关库。

tests Latest Stable Version Total Downloads Latest Unstable Version License

特点

  • 接受几乎所有类型的WGS84 地理坐标作为坐标。 »
  • 支持 23种不同的椭球体,如果需要,可以轻松提供新的椭球体。 »
  • 转换并将十进制度数坐标转换为十进制度分或度分秒坐标。 »
  • 转换十进制度数坐标到通用横轴墨卡托(UTM)投影。 »
  • 使用平坦、大圆、哈弗辛或文森蒂算法,通过距离算法计算两点之间的距离(默认为米),单位可以是公里、英里或英尺。 »
  • 计算从起点坐标到目的地坐标的初始和最终航向(度数)。 »
  • 计算从起点坐标到目的地坐标的初始和最终方位点(方向),更多信息请参考维基百科»
  • 计算起点和目的地坐标之间的中点(坐标)。 »
  • 根据给定的度数航向和米距离计算目的地点(坐标)。 »
  • 将坐标编码为geo hash字符串,并将其解码为坐标,更多信息请参考维基百科geohash.org»
  • 通过10:10算法编码坐标。 »
  • 多边形类提供检查一个点(坐标)是否在多边形内部或在多边形边界上的方法。 »
  • ... 更多功能即将推出 ...

安装

Geotools 可在 Packagist 上找到。安装 Geotools 的推荐方式是通过 composer

在命令行中运行以下命令

php composer require laravie/geotools=~1.0

用法 & API

坐标 & 椭球体

默认的地球椭球体是 WGS84,坐标以十进制度数表示。

以下是可用的椭球体:AIRYAUSTRALIAN_NATIONALBESSEL_1841BESSEL_1841_NAMBIACLARKE_1866CLARKE_1880EVERESTFISCHER_1960_MERCURYFISCHER_1968GRS_1967GRS_1980HELMERT_1906HOUGHINTERNATIONALKRASSOVSKYMODIFIED_AIRYMODIFIED_EVERESTMODIFIED_FISCHER_1960SOUTH_AMERICAN_1969WGS60WGS66WGS72WGS84

如果您需要使用其他椭球体,只需创建一个如下所示的数组

<?php

$myEllipsoid = \League\Geotools\Coordinate\Ellipsoid::createFromArray([
    'name' => 'My Ellipsoid', // The name of the Ellipsoid
    'a'    => 123.0, // The semi-major axis (equatorial radius) in meters
    'invF' => 456.0 // The inverse flattening
]);

Geotools是为与Geocoder一起使用而构建的。这意味着可以直接使用\Geocoder\Model\Address,也可以使用一个字符串或一个简单的包含其纬度和经度的数组

它支持有效和可接受的地理坐标,如下所示

  • 40:26:46N,079:56:55W
  • 40:26:46.302N 079:56:55.903W
  • 40°26′47″N 079°58′36″W
  • 40d 26′ 47″ N 079d 58′ 36″ W
  • 40.446195N 79.948862W
  • 40.446195, -79.948862
  • 40° 26.7717, -79° 56.93172

纬度小于-90.0或大于90.0度通过\League\Geotools\Coordinate\Coordinate::normalizeLatitude()进行封顶
经度小于-180.0或大于180.0度通过\League\Geotools\Coordinate\Coordinate::normalizeLongitude()进行折叠

<?php

use League\Geotools\Coordinate\Coordinate;
use League\Geotools\Coordinate\Ellipsoid;

// from an \Geocoder\Model\Address instance within Airy ellipsoid
$coordinate = new Coordinate($geocoderResult, Ellipsoid::createFromName(Ellipsoid::AIRY));
// or in an array of latitude/longitude coordinate within GRS 1980 ellipsoid
$coordinate = new Coordinate([48.8234055, 2.3072664], Ellipsoid::createFromName(Ellipsoid::GRS_1980));
// or in latitude/longitude coordinate within WGS84 ellipsoid
$coordinate = new Coordinate('48.8234055, 2.3072664');
// or in degrees minutes seconds coordinate within WGS84 ellipsoid
$coordinate = new Coordinate('48°49′24″N, 2°18′26″E');
// or in decimal minutes coordinate within WGS84 ellipsoid
$coordinate = new Coordinate('48 49.4N, 2 18.43333E');
// the result will be:
printf("Latitude: %F\n", $coordinate->getLatitude()); // 48.8234055
printf("Longitude: %F\n", $coordinate->getLongitude()); // 2.3072664
printf("Ellipsoid name: %s\n", $coordinate->getEllipsoid()->getName()); // WGS 84
printf("Equatorial radius: %F\n", $coordinate->getEllipsoid()->getA()); // 6378136.0
printf("Polar distance: %F\n", $coordinate->getEllipsoid()->getB()); // 6356751.317598
printf("Inverse flattening: %F\n", $coordinate->getEllipsoid()->getInvF()); // 298.257224
printf("Mean radius: %F\n", $coordinate->getEllipsoid()->getArithmeticMeanRadius()); // 6371007.772533
// it's also possible to modify the coordinate without creating an other coodinate
$coordinate->setFromString('40°26′47″N 079°58′36″W');
printf("Latitude: %F\n", $coordinate->getLatitude()); // 40.446388888889
printf("Longitude: %F\n", $coordinate->getLongitude()); // -79.976666666667

转换

它提供将WGS84坐标的十进制度转换为度分秒或WGS84坐标的十进制度分的方法(和别名)。您可以轻松地格式化输出字符串。

您还可以将它们转换为通用横轴墨卡托(UTM)投影(覆盖挪威西南海岸和斯瓦尔巴地区)。

<?php

$geotools   = new \League\Geotools\Geotools();
$coordinate = new \League\Geotools\Coordinate\Coordinate('40.446195, -79.948862');
$converted  = $geotools->convert($coordinate);
// convert to decimal degrees without and with format string
printf("%s\n", $converted->toDecimalMinutes()); // 40 26.7717N, -79 56.93172W
printf("%s\n", $converted->toDM('%P%D°%N %p%d°%n')); // 40°26.7717 -79°56.93172
// convert to degrees minutes seconds without and with format string
printf("%s\n", $converted->toDegreesMinutesSeconds('<p>%P%D:%M:%S, %p%d:%m:%s</p>')); // <p>40:26:46, -79:56:56</p>
printf("%s\n", $converted->toDMS()); // 40°26′46″N, 79°56′56″W
// convert in the UTM projection (standard format)
printf("%s\n", $converted->toUniversalTransverseMercator()); // 17T 589138 4477813
printf("%s\n", $converted->toUTM()); // 17T 589138 4477813 (alias)

以下是映射

距离

它提供方法来计算两点之间使用扁平(性能最高)、大圆哈弗西涅文森特(最精确)算法的(默认)、千米英里英尺之间的距离。

这些坐标应在同一椭球体中。

<?php

$geotools = new \League\Geotools\Geotools();
$coordA   = new \League\Geotools\Coordinate\Coordinate([48.8234055, 2.3072664]);
$coordB   = new \League\Geotools\Coordinate\Coordinate([43.296482, 5.36978]);
$distance = $geotools->distance()->setFrom($coordA)->setTo($coordB);

printf("%s\n",$distance->flat()); // 659166.50038742 (meters)
printf("%s\n",$distance->greatCircle()); // 659021.90812846
printf("%s\n",$distance->in('km')->haversine()); // 659.02190812846
printf("%s\n",$distance->in('mi')->vincenty()); // 409.05330679648
printf("%s\n",$distance->in('ft')->flat()); // 2162619.7519272

它提供方法来计算以度为单位的第一和最终方位,第一和最终方位方向中间点目标点。中间点和目标点返回一个具有相同椭球体的\League\Geotools\Coordinate\Coordinate对象。

<?php

$geotools = new \League\Geotools\Geotools();
$coordA   = new \League\Geotools\Coordinate\Coordinate([48.8234055, 2.3072664]);
$coordB   = new \League\Geotools\Coordinate\Coordinate([43.296482, 5.36978]);
$vertex    =  $geotools->vertex()->setFrom($coordA)->setTo($coordB);

printf("%d\n", $vertex->initialBearing()); // 157 (degrees)
printf("%s\n", $vertex->initialCardinal()); // SSE (SouthSouthEast)
printf("%d\n", $vertex->finalBearing()); // 160 (degrees)
printf("%s\n", $vertex->finalCardinal()); // SSE (SouthSouthEast)

$middlePoint = $vertex->middle(); // \League\Geotools\Coordinate\Coordinate
printf("%s\n", $middlePoint->getLatitude()); // 46.070143125815
printf("%s\n", $middlePoint->getLongitude()); // 3.9152401085931

$destinationPoint = $geotools->vertex()->setFrom($coordA)->destination(180, 200000); // \League\Geotools\Coordinate\Coordinate
printf("%s\n", $destinationPoint->getLatitude()); // 47.026774650075
printf("%s\n", $destinationPoint->getLongitude()); // 2.3072664

Geohash

它提供方法来获取一个坐标的地理哈希及其边界框坐标(西南 & 东北)以及地理哈希的坐标及其边界框坐标(西南 & 东北)。

<?php

$geotools       = new \League\Geotools\Geotools();
$coordToGeohash = new \League\Geotools\Coordinate\Coordinate('43.296482, 5.36978');

// encoding
$encoded = $geotools->geohash()->encode($coordToGeohash, 4); // 12 is the default length / precision
// encoded
printf("%s\n", $encoded->getGeohash()); // spey
// encoded bounding box
$boundingBox = $encoded->getBoundingBox(); // array of \League\Geotools\Coordinate\CoordinateInterface
$southWest   = $boundingBox[0];
$northEast   = $boundingBox[1];
printf("http://www.openstreetmap.org/?minlon=%s&minlat=%s&maxlon=%s&maxlat=%s&box=yes\n",
    $southWest->getLongitude(), $southWest->getLatitude(),
    $northEast->getLongitude(), $northEast->getLatitude()
); // http://www.openstreetmap.org/?minlon=5.2734375&minlat=43.2421875&maxlon=5.625&maxlat=43.41796875&box=yes

// decoding
$decoded = $geotools->geohash()->decode('spey61y');
// decoded coordinate
printf("%s\n", $decoded->getCoordinate()->getLatitude()); // 43.296432495117
printf("%s\n", $decoded->getCoordinate()->getLongitude()); // 5.3702545166016
// decoded bounding box
$boundingBox = $decoded->getBoundingBox(); //array of \League\Geotools\Coordinate\CoordinateInterface
$southWest   = $boundingBox[0];
$northEast   = $boundingBox[1];
printf("http://www.openstreetmap.org/?minlon=%s&minlat=%s&maxlon=%s&maxlat=%s&box=yes\n",
    $southWest->getLongitude(), $southWest->getLatitude(),
    $northEast->getLongitude(), $northEast->getLatitude()
); // http://www.openstreetmap.org/?minlon=5.3695678710938&minlat=43.295745849609&maxlon=5.3709411621094&maxlat=43.297119140625&box=yes

10:10

使用一个包含防止输入错误特性的10字符代码以10米精度表示位置。有关算法的更多信息,请参阅这里

<?php

$tenten = new \League\Geotools\Tests\Geohash\TenTen;
$tenten->encode(new Coordinate([51.09559, 1.12207])); // MEQ N6G 7NY5

顶点

表示具有方向的线段。您可以使用它来确定两个顶点是否在同一直线上。

<?php
	$vertexA->setFrom(48.8234055);
	$vertexA->setTo(2.3072664);

	$vertexB->setFrom(48.8234055);
	$vertexB->setTo(2.3072664);
	$vertexA->isOnSameLine($vertexB);

多边形

它帮助您知道一个点(坐标)是否在多边形内或在多边形的边界上,以及这个点是否在多边形的顶点上。

首先您需要创建多边形,您可以提供

  • 一个数组的数组
  • 一个Coordinate的数组
  • CoordinateCollection
<?php

$polygon = new \League\Geotools\Polygon\Polygon([
    [48.9675969, 1.7440796],
    [48.4711003, 2.5268555],
    [48.9279131, 3.1448364],
    [49.3895245, 2.6119995],
]);

$polygon->setPrecision(5); // set the comparision precision
$polygon->pointInPolygon(new \League\Geotools\Coordinate\Coordinate([49.1785607, 2.4444580])); // true
$polygon->pointInPolygon(new \League\Geotools\Coordinate\Coordinate([49.1785607, 5])); // false
$polygon->pointOnBoundary(new \League\Geotools\Coordinate\Coordinate([48.7193486, 2.13546755])); // true
$polygon->pointOnBoundary(new \League\Geotools\Coordinate\Coordinate([47.1587188, 2.87841795])); // false
$polygon->pointOnVertex(new \League\Geotools\Coordinate\Coordinate([48.4711003, 2.5268555])); // true
$polygon->pointOnVertex(new \League\Geotools\Coordinate\Coordinate([49.1785607, 2.4444580])); // false
$polygon->getBoundingBox(); // return the BoundingBox object

单元测试

要运行单元测试,您需要cURL扩展和一组依赖项,您可以使用Composer安装它们

$ php composer.phar install --dev

安装后,只需启动以下命令

$ phpunit --coverage-text

鸣谢

贡献者行为准则

作为此项目的贡献者和维护者,我们承诺尊重所有通过报告问题、发布功能请求、更新文档、提交拉取请求或补丁以及其他活动做出贡献的人。

我们致力于确保每个人,无论经验水平、性别、性别认同和表达、性取向、残疾、个人外貌、体型、种族、年龄或宗教,都能在这项项目中享受到一个无骚扰的参与体验。

参与者不可接受的行为包括使用性语言或图像、侮辱性评论或人身攻击、捣乱、公开或私下骚扰、侮辱或其他不专业行为。

项目负责人有权和义务移除、编辑或拒绝与这一行为准则不符的评论、提交、代码、维基编辑、问题和其他贡献。不遵守行为准则的项目负责人可能会被从项目团队中移除。

对于滥用、骚扰或其他不可接受的行为,可以通过提交问题或联系项目负责人之一或多个来报告。

本行为准则改编自贡献者公约,版本1.0.0,可在http://contributor-covenant.org/version/1/0/0/找到。

许可证

Geotools是在MIT许可证下发布的。有关详细信息,请参阅捆绑的LICENSE文件。