Flutter几何计算插件s2geometry_dart的使用

Flutter几何计算插件s2geometry_dart的使用

简介

S2 Geometry是由Google开发的强大库,它提供了一个在球面上处理几何数据的框架。S2 Geometry的核心思想是将地球表面投影到一个球体上,并将其划分为称为S2单元格的分层网格。这种系统使得高效的索引、查询和空间操作成为可能,如搜索附近的位置、面积计算等。S2单元格通过递归地将立方体的面投影到球体上来创建,它们在不同尺度上保持精度,从全球到非常精细的细节。

Google的S2 Geometry库被广泛应用于大规模应用中,包括Google地图,并且被广泛用于解决复杂的地理空间问题。

了解更多:

参考文献

移植计划

  1. 目标

    • 功能完整性:Dart版本应能够实现JavaScript版本的所有功能。
    • 使用方便性:提高使用的便利性,确保用户无需深入了解内部实现即可轻松使用该包。
  2. 准备与分析

    • 由于JavaScript版本的所有函数都组合在一个脚本中,因此将其拆分为多个函数以提高可维护性。
    • 为每个函数编写测试用例,确保每个函数在移植后仍然可用。
  3. 移植过程

    • 分模块移植:
      • 按照JavaScript版本的模块结构逐步移植模块。
      • 为每个模块编写测试用例,确保移植完成后模块可用。
      • 在移植每个函数后进行集成测试,以确保新模块不会破坏现有功能。
    • 集成测试:
      • 在移植每个函数后进行集成测试,以确保新模块不会破坏现有功能。
  4. 测试

    • 使用test包确保所有移植的功能按预期工作。每个函数都有对应的单元测试,所有函数移植完成后进行集成测试。

任务难度

  • 语言特性差异:JavaScript是一种动态类型语言,而Dart是一种静态类型语言,因此需要检查并重构每个变量和函数的类型。
  • 数据结构:JavaScript中常用的ArrayObject在Dart中分别对应ListMap,需要重写代码以适应Dart的方法和特性。
  • **使用BigInt声明S2单元格ID以解决超过64位数字的问题。

用户可用的功能

以下功能使用户能够执行地理计算和转换:

latLngToXYZ(LatLng latLng) // 将'lat', 'lng'转换为s2点

fromLatLng(LatLng latLng, int level) // 将'lat', 'lng'转换为s2单元格

latLngToId(lat, lng, [level]) // 直接将'lat', 'lng', 'level'转换为S2单元格ID。'level'默认为15,例如latLngToId(lat, lng, level = 10)来指定'level'。

latLngToKey(lat, lng, level) // 将'lat', 'lng', 'level'转换为S2单元格键。

latLngToNeighborKeys(lat, lng, level) // 将'lat', 'lng'转换为相邻的希尔伯特四叉树键。

getLatLng() // 将s2单元格转换为'lat', 'lng',这是一个非静态方法,因此需要创建S2Cell实例。

getCornerLatLngs() // 获取s2单元格的4个角点的'lat', 'lng',这是一个非静态方法,因此需要创建S2Cell实例。

getNeighbors() // 获取4个相邻的S2单元格,这是一个非静态方法,因此需要创建S2Cell实例。

keyToLatLng(key) // 将S2单元格键转换回'lat', 'lng'。

fromHilbertQuadKey(key) // 将键转换为s2单元格。

keyToId(key) // 将S2单元格键转换为S2单元格ID。

idToLatLng(id) // 将S2单元格ID转换回'lat', 'lng'。

idToKey(id) // 将S2单元格ID转换为键。

latLngToNeighborKeys(lat, lng, level) // 获取给定'lat'和'lng'在指定级别下的S2单元格邻居键列表。

nextKey(key) // 获取当前键之后的下一个S2单元格键。

prevKey(key) // 获取当前键之前的上一个S2单元格键。

stepKey(key, step) // 将当前键向前或向后移动指定步数。

facePosLevelToId(face, posS, levelN) // 获取S2单元格的面和四叉树列表,请参阅示例演示。

getFaceAndQuads() // 获取S2单元格的面和四叉树列表,请参阅示例演示。

toHilbertQuadkey() // 将S2单元格转换为希尔伯特四叉树键,请参阅示例演示。

使用说明

pubspec.yaml文件中添加依赖:

dependencies:
    s2geometry_dart: ^0.0.3

示例

以下是一个完整的示例代码,展示了如何使用s2geometry_dart插件进行地理计算和转换:

import 'package:s2geometry_dart/s2geometry_dart.dart';

void main() {
  /// 定义纬度、经度和级别
  var lat = 40.2574448;
  var lng = -111.7089464;
  var latlng = LatLng(lat, lng);
  var level = 15;

  /// 将纬度、经度转换为s2点
  var point = S2.latLngToXYZ(latlng);
  print('point: $point');

  /// 将纬度、经度转换为s2单元格
  var s2cellFromLatLng = S2.fromLatLng(latlng, level);
  print('s2cellFromLatLng: $s2cellFromLatLng');
  
  /// 将纬度、经度、级别转换为ID
  var idFromLatLng = S2.latLngToId(lat, lng); // 级别默认为15,使用'level = 10'来指定级别,例如latLngToId(lat, lng, level = 10)
  print('idFromLatLng: $idFromLatLng');

  /// 将纬度、经度、级别转换为键
  var key = S2.latLngToKey(lat, lng, level);
  print('key: $key');

  /// 将纬度、经度转换为相邻的希尔伯特四叉树键
  var neighborsKeysFromLatLng = S2.latLngToNeighborKeys(lat, lng, level);
  print('neighborsKeysFromLatLng: $neighborsKeysFromLatLng');

  /// 将s2单元格转换为纬度、经度
  var latLngFromS2Cell = s2cellFromLatLng.getLatLng(); /// getLatLng() 是非静态方法,所以需要创建S2Cell实例
  print('latLngFromS2Cell: $latLngFromS2Cell');

  /// 获取s2单元格的4个角点的纬度、经度
  var corners = s2cellFromLatLng.getCornerLatLngs(); /// getCornerLatLngs() 是非静态方法,所以需要创建S2Cell实例
  print('corners: $corners');

  /// 获取4个相邻的S2单元格
  var neighborsFromS2Cell = s2cellFromLatLng.getNeighbors(); /// getNeighbors() 是非静态方法,所以需要创建S2Cell实例
  print('neighborsFromS2Cell: $neighborsFromS2Cell');
  
  /// 将键转换为纬度、经度、级别
  var latLngFromKey = S2.keyToLatLng(key);
  print('latLngFromKey: $latLngFromKey');

  /// 将键转换为s2单元格
  var s2cellFromKey = S2.fromHilbertQuadKey(key);
  print('s2cellFromKey: $s2cellFromKey');

  /// 将键转换为ID
  var idFromKey = S2.keyToId(key);
  print('idFromKey: $idFromKey');

  /// 将ID转换为纬度、经度、级别
  var idToLatLng = S2.idToLatLng(idFromKey);
  print('idToLatLng: $idToLatLng');

  /// 将ID转换为键
  var keyFromId = S2.idToKey(idFromKey);
  print('keyFromId: $keyFromId');

  /// 基于纬度、经度、级别获取邻居
  var neighbors = S2.latLngToNeighborKeys(lat, lng, level);
  print('neighbors: $neighbors');

  /// 当前键的下一个键
  var nextKey = S2.nextKey(key);
  print('nextKey: $nextKey');

  /// 当前键的前一个键
  var prevKey = S2.prevKey(key);
  print('prevKey: $prevKey');

  /// 向后移动十个键
  var backTenKeys = S2.stepKey(key, -10);
  print('backTenKeys: $backTenKeys');
  
  /// 将面、位置和级别转换为S2单元格ID
  var quadkey = '4/0010023000';
  var parts = quadkey.split('/');
  var face = int.parse(parts[0]);
  var posS = parts[1];
  var levelN = posS.length;

  var idFromFacePosLevel = S2.facePosLevelToId(face, posS, levelN);
  print('idFromFacePosLevel: $idFromFacePosLevel');

  /// 获取S2单元格的面和四叉树列表
  S2 cell = S2.fromLatLng(latlng, level); // cell = [4, [1107, 8497], 15] 面,ij ,级别
  var faceAndQuads = cell.getFaceAndQuads(); 
  print('faceAndQuads: $faceAndQuads');

  /// 将S2单元格转换为希尔伯特四叉树键
  var hilbertQuadkey = cell.toHilbertQuadkey();
  print('hilbertQuadkey: $hilbertQuadkey');
}

更多关于Flutter几何计算插件s2geometry_dart的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter几何计算插件s2geometry_dart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


s2geometry_dart 是一个用于 Flutter 的 Dart 插件,它是 Google S2 Geometry Library 的 Dart 移植版本。S2 Geometry 是一个用于处理地理空间数据的库,特别适用于处理球面上的几何计算,比如计算两个地理位置之间的距离、判断一个点是否在多边形内、生成地理空间的索引等。

安装

在你的 pubspec.yaml 文件中添加 s2geometry_dart 依赖:

dependencies:
  s2geometry_dart: ^1.0.0

然后运行 flutter pub get 来安装依赖。

基本使用

1. 创建 S2CellId

S2CellId 是 S2 中的基本单元,代表地球上的一个区域。你可以通过经纬度来创建一个 S2CellId

import 'package:s2geometry_dart/s2geometry_dart.dart';

void main() {
  // 创建一个 S2CellId,使用经纬度
  var s2CellId = S2CellId.fromLatLng(LatLng(37.4219999, -122.0840575));

  print('S2CellId: ${s2CellId.id}');
}

2. 计算两个点之间的距离

你可以使用 S2LatLng 来计算两个经纬度点之间的距离。

import 'package:s2geometry_dart/s2geometry_dart.dart';

void main() {
  var point1 = LatLng(37.4219999, -122.0840575); // 谷歌总部
  var point2 = LatLng(34.052235, -118.243683);   // 洛杉矶市中心

  var distance = S2LatLng.fromDegrees(point1.latitude, point1.longitude)
      .getDistance(S2LatLng.fromDegrees(point2.latitude, point2.longitude));

  print('Distance: ${distance * 1000} meters'); // 输出距离(单位:米)
}

3. 判断点是否在多边形内

你可以使用 S2Polygon 来判断一个点是否在多边形内。

import 'package:s2geometry_dart/s2geometry_dart.dart';

void main() {
  // 创建一个多边形
  var polygon = S2Polygon([
    LatLng(37.4219999, -122.0840575),
    LatLng(37.4219999, -122.0840576),
    LatLng(37.4219998, -122.0840576),
    LatLng(37.4219998, -122.0840575),
  ]);

  // 要判断的点
  var point = LatLng(37.4219999, -122.0840575);

  // 判断点是否在多边形内
  var isInside = polygon.contains(point);

  print('Is point inside polygon? $isInside');
}

4. 生成 S2CellId 的覆盖区域

你可以使用 S2RegionCoverer 来生成一组 S2CellId,这些 S2CellId 可以覆盖指定的区域。

import 'package:s2geometry_dart/s2geometry_dart.dart';

void main() {
  // 创建一个 S2RegionCoverer
  var coverer = S2RegionCoverer();

  // 设置最大和最小层级
  coverer.maxCells = 10;
  coverer.minLevel = 10;
  coverer.maxLevel = 15;

  // 创建一个矩形区域
  var rect = S2LatLngRect.fromLatLng(S2LatLng.fromDegrees(37.4219999, -122.0840575),
                                     S2LatLng.fromDegrees(37.4319999, -122.0940575));

  // 生成覆盖区域的 S2CellId
  var covering = coverer.getCovering(rect);

  print('Covering cells: ${covering.length}');
}
回到顶部