Flutter地理编码与逆地理编码插件jeodezi的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter地理编码与逆地理编码插件jeodezi的使用

Jeodezi

Jeodezi是基于Dart语言的库,其Kotlin版本为Jeodezi,该库基于Chris Veness的出色工作Geodesy Functions。Geodesy Functions用JavaScript编写,包含许多用于以下计算的函数:

  • 地理计算(距离、方位角等),涵盖球形地球和椭球体地球模型,以及基于三角学和向量的方法。
  • 椭球体地球坐标系统,涵盖历史基准面和现代大地参考框架(TRFs)。
  • 映射功能,包括UTM/MGRS和英国OS网格参考。

GitHub license

快速链接

  • 安装
  • 使用
  • 示例
  • 贡献
  • 许可证

安装

在终端中运行以下代码:

dart pub add jeodezi

或者将以下代码添加到你的pubspec.yaml文件中:

dependencies:
    jeodezi: ^latest_version

使用

以下是Jeodezi库的一些基本用法示例。

大圆计算
距离

要计算两个地理坐标的距离,可以使用distance方法。此方法使用Haversine公式计算两点之间的距离。结果将以公里为单位的大圆距离。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final greatCircle = GreatCircle();
final distance = greatCircle.distance(istCoordinates, jfkCoordinates);

如果需要以海里为单位的距离,可以使用相同的参数调用distanceInNm方法。

方位角

此方法用于初始方位角(有时称为前方位角),如果沿大圆弧直线前进,将从起点到达终点。方位角范围为0°至360°。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final greatCircle = GreatCircle();
final bearing = greatCircle.bearing(istCoordinates, jfkCoordinates);
中点

此方法计算大圆上起点和终点之间的中点。结果类型为Coordinate。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final greatCircle = GreatCircle();
final midpoint = greatCircle.midpoint(istCoordinates, jfkCoordinates);
目的地

此方法计算给定起点、初始方位角和距离的情况下,沿最短距离大圆弧行驶的目的地点和最终方位角。结果类型为Coordinate。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final greatCircle = GreatCircle();
final bearing = greatCircle.bearing(istCoordinates, jfkCoordinates);
final distance = 1000; // km
final destination = greatCircle.destination(istCoordinates, distance, bearing); // 在伊斯坦布尔机场和肯尼迪机场之间大圆上的第1000公里处的点坐标
中间点

此函数返回起点和终点之间给定分数处的点。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final fraction = 0.25;
final intermediate = greatCircle.intermediate(istCoordinates, jfkCoordinates, fraction);
交点

此函数返回由第一点开始的第一方位角和第二点开始的第二方位角定义的两条路径的交点。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final fcoCoordinates = Coordinate(41.8002778,12.2388889); // 罗马菲乌米奇诺机场的坐标
final bearingFromIstanbulToWest = 270.0;
final bearingFromRomeToNorthEast = 45.0;
final intersection = greatCircle.intersection(istCoordinates, bearingFromIstanbulToWest, fcoCoordinates, bearingFromRomeToNorthEast);
横跨距离

此函数返回当前点到起点和终点之间的大圆的距离。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final fcoCoordinates = Coordinate(41.8002778,12.2388889); // 罗马菲乌米奇诺机场的坐标
final crossTrackDistanceInKm = greatCircle.crossTrackDistance(fcoCoordinates, istCoordinates, jfkCoordinates);
沿线距离

此函数返回当前点沿从起点出发朝向终点路径的距离。即,如果从当前点绘制一条垂线到路径(大圆),则沿线距离是从起点到垂线与路径交叉点的距离。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final fcoCoordinates = Coordinate(41.8002778,12.2388889); // 罗马菲乌米奇诺机场的坐标
final alongTrackDistanceTo = greatCircle.alongTrackDistanceTo(fcoCoordinates, istCoordinates, jfkCoordinates);
最大纬度

此函数返回在给定方位角下从起点出发沿大圆行进时达到的最大纬度(“克莱罗特公式”)。对于最小纬度(南半球),取负值。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final bearingFromIstanbulToWest = 270.0;
final maxLatitude = greatCircle.maxLatitude(istCoordinates, bearingFromIstanbulToWest);
穿越平行线

此函数返回由两个点定义的大圆穿过给定纬度的成对子午线。如果大圆未到达给定纬度,则返回null。

final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
final latitude = 60.0; // 表示北纬60度
final crossingParallels = greatCircle.crossingParallels(istCoordinates, jfkCoordinates, latitude);

示例

以下是一个完整的Flutter应用程序示例,展示了如何使用Jeodezi库进行地理计算。

import 'package:flutter/material.dart';
import 'package:jeodezi/jeodezi.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const ExampleHomeScreen(),
    );
  }
}

class ExampleHomeScreen extends StatefulWidget {
  const ExampleHomeScreen({Key? key}) : super(key: key);

  [@override](/user/override)
  State<ExampleHomeScreen> createState() => _ExampleHomeScreenState();
}

class _ExampleHomeScreenState extends State<ExampleHomeScreen> {
  final greatCircle = GreatCircle();
  final istCoordinates = Coordinate(41.28111111, 28.75333333); // 伊斯坦布尔机场的坐标
  final jfkCoordinates = Coordinate(40.63980103, -73.77890015); // 纽约肯尼迪机场的坐标
  final fcoCoordinates = Coordinate(41.8002778, 12.2388889); // 罗马菲乌米奇诺机场的坐标
  final sfoCoordinates = Coordinate(37.615223, -122.389977); // 旧金山国际机场的坐标

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            const Center(
              child: Text(
                "Jeodezi Functions",
                style: TextStyle(fontSize: 40),
              ),
            ),
            ElevatedButton(
                onPressed: () {
                  final distance =
                      greatCircle.distance(istCoordinates, jfkCoordinates);
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Great Circle distance between Istanbul Airport and JFK Airport is ${distance.toStringAsFixed(2)} km"),
                  ));
                },
                child: const Text("Calculate Distance")),
            ElevatedButton(
                onPressed: () {
                  final distance =
                      greatCircle.distanceInNm(istCoordinates, jfkCoordinates);
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Great Circle distance between Istanbul Airport and JFK Airport is ${distance.toStringAsFixed(2)} nm"),
                  ));
                },
                child: const Text("Calculate Distance (Nm)")),
            ElevatedButton(
                onPressed: () {
                  final bearing =
                      greatCircle.bearing(istCoordinates, jfkCoordinates);
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Initial bearing for great circle between Istanbul Airport and JFK Airport is ${bearing.toStringAsFixed(2)} degrees"),
                  ));
                },
                child: const Text("Bearing")),
            ElevatedButton(
                onPressed: () {
                  final bearing =
                      greatCircle.finalBearing(istCoordinates, jfkCoordinates);
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Final bearing for great circle between Istanbul Airport and JFK Airport is ${bearing.toStringAsFixed(2)} degrees"),
                  ));
                },
                child: const Text("Final Bearing")),
            ElevatedButton(
                onPressed: () {
                  final midpoint =
                      greatCircle.midpoint(istCoordinates, jfkCoordinates);
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Midpoint's coordinates of great circle between Istanbul Airport and JFK Airport are $midpoint "),
                  ));
                },
                child: const Text("Midpoint")),
            ElevatedButton(
                onPressed: () {
                  const fraction = 1.0;
                  final intermediate = greatCircle.intermediate(
                      startPoint: istCoordinates,
                      endPoint: jfkCoordinates,
                      fraction: fraction);

                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "For $fraction fraction, intermediate point's coordinates of great circle between Istanbul Airport and JFK Airport are $intermediate "),
                  ));
                },
                child: const Text("Intermediate")),
            ElevatedButton(
                onPressed: () {
                  const bearingFromIstanbulToWest = 270.0;
                  const bearingFromRomeToNorthEast = 45.0;
                  final intersection = greatCircle.intersection(
                      firstPoint: istCoordinates,
                      firstBearing: bearingFromIstanbulToWest,
                      secondPoint: fcoCoordinates,
                      secondBearing: bearingFromRomeToNorthEast);

                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Intersection coordinates of from Istanbul Airport with bearing $bearingFromIstanbulToWest and from Roma Fiumicino Airport with bearing $bearingFromRomeToNorthEast is $intersection "),
                  ));
                },
                child: const Text("Intersection")),
            ElevatedButton(
                onPressed: () {
                  const bearingFromIstanbulToWest = 270.0;
                  const distanceInKm = 168.0; // 约100海里
                  final destination = greatCircle.destination(
                    startPoint: istCoordinates,
                    bearing: bearingFromIstanbulToWest,
                    distance: distanceInKm,
                  );

                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "The destination coordinates of $distanceInKm km away from Istanbul Airport on bearing $bearingFromIstanbulToWest is $destination"),
                  ));
                },
                child: const Text("Destination")),
            ElevatedButton(
                onPressed: () {
                  final crossTrackDistance = greatCircle.crossTrackDistance(
                    currentPoint: fcoCoordinates,
                    startPoint: istCoordinates,
                    endPoint: jfkCoordinates,
                  );

                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Distance from Rome Fiumicino Airport to great circle between Istanbul Airport and JFK Airport is ${crossTrackDistance.toStringAsFixed(2)} km "),
                  ));
                },
                child: const Text("Cross Track")),
            ElevatedButton(
                onPressed: () {
                  final alongTrackDistanceTo = greatCircle.alongTrackDistanceTo(
                    currentPoint: fcoCoordinates,
                    startPoint: istCoordinates,
                    endPoint: jfkCoordinates,
                  );

                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "Distance from Istanbul Airport to the point where Roma Fiumicino Airport crosses great circle between Istanbul Airport and JFK Airport is ${alongTrackDistanceTo.toStringAsFixed(2)} km "),
                  ));
                },
                child: const Text("Along Track")),
            ElevatedButton(
                onPressed: () {
                  const bearingFromIstanbulToWest = 270.0;
                  final maxLatitude = greatCircle.maxLatitude(
                    startPoint: istCoordinates,
                    bearing: bearingFromIstanbulToWest,
                  );

                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "The maximum latitude of the path from Istanbul Airport on bearing $bearingFromIstanbulToWest is $maxLatitude degrees"),
                  ));
                },
                child: const Text("Max Latitudes")),
            ElevatedButton(
                onPressed: () {
                  const latitude = 70.0; // 表示北纬70度
                  final crossingParallels = greatCircle.crossingParallels(
                    startPoint: istCoordinates,
                    endPoint: sfoCoordinates,
                    latitude: latitude,
                  );
                  if (crossingParallels.isEmpty) {
                    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
                      content: Text(
                          "The great circle between Istanbul Airport and SFO Airport does not cross latitude $latitude"),
                    ));
                  } else {
                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                      content: Text(
                          "The great circle between Istanbul Airport and SFO Airport crosses latitude $latitude at longitudes ${crossingParallels[0]} and ${crossingParallels[1]}"),
                    ));
                  }
                },
                child: const Text("Crossing Parallels")),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter地理编码与逆地理编码插件jeodezi的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地理编码与逆地理编码插件jeodezi的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter应用中使用geodezi插件进行地理编码与逆地理编码的代码示例。geodezi是一个用于地理编码(根据地址获取经纬度)和逆地理编码(根据经纬度获取地址)的Flutter插件。不过需要注意的是,geodezi这个名称在Flutter的pub.dev上并不常见,因此我假设你可能指的是类似的插件,比如geocoding。以下示例基于geocoding插件。

首先,确保在pubspec.yaml文件中添加geocoding依赖:

dependencies:
  flutter:
    sdk: flutter
  geocoding: ^2.0.0  # 请检查最新版本号

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

接下来是一个完整的Flutter应用示例,展示了如何使用geocoding插件进行地理编码和逆地理编码:

import 'package:flutter/material.dart';
import 'package:geocoding/geocoding.dart';
import 'package:location/location.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Geocoding Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final Location _location = Location();
  List<Placemark> placemarks = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Geocoding Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: _getCurrentLocation,
              child: Text('Get Current Location'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _geocodeAddress,
              child: Text('Geocode Address'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _reverseGeocode,
              child: Text('Reverse Geocode'),
            ),
            SizedBox(height: 20),
            Text(
              placemarks.isNotEmpty
                  ? placemarks.map((e) => e.locality ?? '').join('\n')
                  : 'No placemarks found',
              style: TextStyle(fontSize: 18),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _getCurrentLocation() async {
    bool serviceEnabled;
    LocationPermission permission;

    // Test if location services are enabled.
    serviceEnabled = await _location.serviceEnabled();
    if (!serviceEnabled) {
      return Future.error('Location services are disabled.');
    }

    permission = await _location.requestPermission();
    if (permission == LocationPermission.denied) {
      return Future.error('Location permissions are denied');
    }

    if (permission == LocationPermission.deniedForever) {
      return Future.error(
          'Location permissions are permanently denied, we cannot request permissions.');
    }

    // Location permission granted
    Position position = await _location.getLocation();
    print('Current Position: ${position?.latitude}, ${position?.longitude}');
    // Here you can call _reverseGeocode with position.latitude and position.longitude
  }

  Future<void> _geocodeAddress() async {
    String address = '1600 Amphitheatre Parkway, Mountain View, CA';
    List<Placemark> placemarks = await placemarkFromAddress(address);

    setState(() {
      this.placemarks = placemarks;
    });
  }

  Future<void> _reverseGeocode() async {
    // Example coordinates (you can replace with actual coordinates)
    double latitude = 37.4219999;
    double longitude = -122.0840575;
    List<Placemark> placemarks = await placemarkFromCoordinates(latitude, longitude);

    setState(() {
      this.placemarks = placemarks;
    });
  }
}

在这个示例中:

  1. getCurrentLocation函数用于获取当前设备的地理位置(经纬度)。
  2. geocodeAddress函数用于根据地址进行地理编码,返回相应的经纬度信息。
  3. reverseGeocode函数用于根据经纬度进行逆地理编码,返回相应的地址信息。

注意:

  • 在实际使用中,请确保处理错误情况,例如位置服务不可用或权限被拒绝。
  • geocoding插件依赖于设备的定位服务和网络连接,因此在实际设备上测试可能更为准确。
  • 地理位置服务可能涉及用户隐私,因此请确保遵循相关的隐私政策和法规。
回到顶部