【问题标题】:Auto Calculate Distance from Geo Coordiates data stored in Firebase Firestore Flutter自动计算与存储在 Firebase Firestore Flutter 中的地理坐标数据的距离
【发布时间】:2020-12-22 08:54:03
【问题描述】:

我已在 firestore 数据库中存储了项目的纬度和经度(字段为:item_latitude 和 item_longitude)。因此,所有项目都有纬度和经度。我可以使用流来获取项目,例如:

  Stream<QuerySnapshot> getItems() async* {
   yield* FirebaseFirestore.instance.collection("items").snapshots();
  }

使用 StreamBuilder 或 FutureBuilder,我可以获得项目的单个属性,例如纬度和经度。 Geolocator有一种计算距离的方法,也是一种未来:

double distance = await geolocator.distanceBetween(lat, long, lat1, long1);

我能够获取用户的当前位置,在这种情况下它是 lat1、long1(这是一个单独的记录)。问题是:Strem getItems 获取纬度和经度流,对于每个项目,我需要参考当前位置计算其当前距离。这意味着,例如在 GridView 中遍历项目时,我需要计算并显示距离。 我以抽象的方式编写了这个问题,以便答案将解决如何基于同步数据流进行异步计算,以便在数据显示在页面的构建部分中时,计算是在外部完成的,因为不是,构建不会接受同步的异步计算。 我的尝试导致了以下结果:第一次尝试:

child: StreamBuilder(
       stream: FetchItems().getItems(),
       builder: (context, snapshot) {
         if (!snapshot.hasData) {
            return Text("KE");
         }
         if (snapshot.hasData) {
         DocumentSnapshot data = snapshot.data.docs[index];
         double lat = data.data()[Str.ITEM_LATITUDE];
         double long = data.data()[Str.ITEM_LATITUDE];
         return 
         Text(getDistance(usersCurrentLocationLat,usersCurrentLocationLong,lat,long).toString());
       //This fails and returns on the Text place holder the following: Instance of 'Future<dynamic>'
     }
    }),

我的第二次尝试如下:

child: StreamBuilder(
       stream: FetchItems().getItems(),
       builder: (context, snapshot) {
         if (!snapshot.hasData) {
            return Text("KE");
         }
         if (snapshot.hasData) {
         DocumentSnapshot data = snapshot.data.docs[index];
         double lat = data.data()[Str.ITEM_LATITUDE];
         double long = data.data()[Str.ITEM_LATITUDE];
         double x = getDistance(usersCurrentLocationLat,usersCurrentLocationLong,lat,long);
         return Text(x.toString());
       //This fails and gives erro: type 'Future<dynamic>' is not a subtype of type 'double'
     }
    }),

进一步调查表明,以下用于获取当前位置并在 iniState 中引用的方法实际上确实获取了值(假设 Gps 在场外启用):

  _getUserCurrentLocation() {
final Geolocator geolocator = Geolocator()..forceAndroidLocationManager;

geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best).then(
  (Position position) {
    setState(
      () {
        _currentPosition = position;
        usersCurrentLocationLat = _currentPosition.latitude;
        usersCurrentLocationLong = _currentPosition.longitude;
     //a system print here returns current location as 0.3714267 32.6134379 (same for the 
     //StreamBuilder)
      },
    );
  },
).catchError((e) {
  print(e);
 });
}

以下是计算距离的方法 - 使用提供的 Geolocator distanceBetween() 方法。

  getDistance(double lat, double long, double lat1, double long1) async {
    return distance = await geolocator.distanceBetween(lat, long, lat1, long1);
  }

  @override
  void initState() {
  super.initState();
  _getUserCurrentLocation();
  }

我将如何遍历获取它们的纬度和经度的项目,计算距离并将其显示在文本上?这是一个普遍的问题,可能的解决方案组合将非常受欢迎。请注意,在 StreamBuilder 中,我实际上可以使用以下命令打印以控制每个坐标:

print("FROM CURRENT LOCATION HERE ----" + usersCurrentLocationLat.toString() +"::::::::" +
      usersCurrentLocationLong.toString());
print("FROM STREAM FROM DB SURE ----" + lat.toString() +"::::::::" + long.toString());

在控制台中为数据库中的所有项目打印为(一个示例):

I/flutter (30351): FROM CURRENT LOCATION HERE ----0.3732317::::::::32.6128083
I/flutter (30351): FROM STREAM FROM DB SURE ----2.12323::::::::2.12323

证明坐标是实际得到的。 主要错误:“Future”类型不是“double”类型的子类型,并且显示距离的文本被拉伸为红色。如果可以,请指导最佳方法 - 它也可能对将来的某人有所帮助。

【问题讨论】:

  • indexsnapshot.data.docs[index] 中使用的是什么?可以使用什么范围的值?
  • 对于流,FetchItems().getItems(),对于每个快照,我们使用如下索引进行迭代: DocumentSnapshot data = snapshot.data.docs[index]; index 来自:StaggeredGridView.countBuilder( itemBuilder: (BuildContext context, int index) 项目显示在网格中。问题出在:类型“Future”不是“double”类型的子类型 - 如如何获取getDistance 中的未来(因为它有等待)并在文本显示中传递与双精度相同的值。
  • index 的值是多少?是 0、100、10000 吗?可以使用什么范围的值?
  • 在网格中,我们首先得到计数,所以如果项目是 3 或 4 或 n,那么我们迭代并显示我们想要的。 itemCount: items.length, 被传递: StaggeredGridView.countBuilder(scrollDirection: Axis.vertical, shrinkWrap: true, controller: scrollController, crossAxisCount: 4, itemCount: items.length, itemBuilder: (BuildContext context, int index)
  • 带有索引的迭代工作完美,这就是为什么打印到控制台工作完美,如果在我的应用程序中选择不同的项目,地理坐标将显示在控制台上,而不是在文本(距离计算基本上不起作用,因为它期待未来。

标签: firebase flutter


【解决方案1】:

我的做法如下:

通过以下方式查询整个文档:

//This a synchronus operation    
final data = await Firestore.instance
            .collection('collection_name')
            .getDocuments();

然后将所有文档传递到一个列表中: 因为DocumentSnapshotList&lt;dynamic&gt;

List doc = data.documents;

现在计算firestore 中每个LatLng 与当前位置LatLng 的距离,然后继续将其附加到一个空的list,在迭代时您可以在gridView 中使用它:

List distanceList=[]; //define and empty list
doc.forEach((e){
double lat=e.data[ITEM_LATITUDE]; //asuming ITEM_LATITUDE is the field name in firestore doc.
double lng=e.data[ITEM_LONGITUDE];//asuming ITEM_LONGITUDE is the field name in firestore doc.
distanceList.add(your distance calculating function); //call this inside an async function if you are using await;
});

我没有使用 Async gelocator.distance,而是使用“haversine”公式在 dart 中编写了一个函数来查找最短函数:

double calculateDistance (double lat1,double lng1,double lat2,double lng2){
    double radEarth =6.3781*( pow(10.0,6.0));
    double phi1= lat1*(pi/180);
    double phi2 = lat2*(pi/180);
    
    double delta1=(lat2-lat1)*(pi/180);
    double delta2=(lng2-lng1)*(pi/180);
    
    double cal1 = sin(delta1/2)*sin(delta1/2)+(cos(phi1)*cos(phi2)*sin(delta2/2)*sin(delta2/2));
    
   double cal2= 2 * atan2((sqrt(cal1)), (sqrt(1-cal1)));
    double distance =radEarth*cal2;
    
    return (distance);
    
}

这是一个synchronous function,在firestoredoc.forEach();列表函数中的代码如下:

List distanceList; //define and empty list
doc.forEach((e){
double lat=e.data[ITEM_LATITUDE]; //asuming ITEM_LATITUDE is the field name in firestore doc.
double lng=e.data[ITEM_LONGITUDE];//asuming ITEM_LONGITUDE is the field name in firestore doc.
double distance = calculateDistance(currentLat,currentLng, lat,lng);
distanceList.add(distance);
});
//Now the distanceList would contain all the shortest distance between 
// current LatLng and all the other LatLng in your firestore documents:

记得清空distanceList,然后再用于存储距离。


整个代码如下;

//Shortest Distance Function definition:
double calculateDistance (double lat1,double lng1,double lat2,double lng2){
double radEarth =6.3781*( pow(10.0,6.0));
double phi1= lat1*(pi/180);
double phi2 = lat2*(pi/180);
    
double delta1=(lat2-lat1)*(pi/180);
double delta2=(lng2-lng1)*(pi/180);
    
double cal1 = sin(delta1/2)*sin(delta1/2)+(cos(phi1)*cos(phi2)*sin(delta2/2)*sin(delta2/2));
    
double cal2= 2 * atan2((sqrt(cal1)), (sqrt(1-cal1)));
double distance =radEarth*cal2;
    
return (distance);
    
}

List distanceList; //list defination

// Call this function every time you want to calculate distance between currentLocation and all location in firestore.
void calculateDistanceAndStore(double currentLat, double currentLng) async{
distanceList=[];p;
final data = await Firestore.instance
            .collection('collection_name')
            .getDocuments();
doc.forEach((e){
double lat=e.data[ITEM_LATITUDE]; //asuming ITEM_LATITUDE is the field name in firestore doc.
double lng=e.data[ITEM_LONGITUDE];//asuming ITEM_LONGITUDE is the field name in firestore doc.
    double distance = calculateDistance(currentLat,currentLng, lat,lng);
    distanceList.add(distance);
    });
}

【讨论】:

  • 让我试试这个——我正在看。请等一下,我测试一下。
  • @ombiro 尝试新代码我已纠正错误,距离以米为单位
  • 您能否提供距离将如何传递给文本小部件?
  • 这样Text('${documentList[0]}',),其中 0 将调用第一个距离,1 将是第二个列表项。
  • 您是否使用ListView 来显示距离?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 2020-06-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多