geo.json网站

Developing apps that rely on geo-coordinates to enable important features must go far beyond simply storing latitude and longitude data. Geo coordinate algorithms that calculate bounds and distances can be a fun brain challenge to implement, though more often than not they become time-consuming distractions from one’s core project.

开发依赖于地理坐标以启用重要功能的应用程序必须远远超出简单存储纬度和经度数据的范围。 计算边界和距离的地理坐标算法可能是实现大脑的一项艰巨挑战,尽管它们经常会变得很耗时,使人们无法专注于核心项目。

In this tutorial, we’re going to look at how we can quickly set up and use an API that has advanced and ready to use Geo Coordinate operations. It doesn’t matter whether you are building a real estate application that’s map-based, a food reviews site that shows the distance a user is from the restaurant, or any other app that will need to work with geo-coordinate data; this tutorial will apply to any such use case.

在本教程中,我们将研究如何快速设置和使用具有高级功能并且可以使用地理坐标操作的API。 无论您是构建基于地图的房地产应用程序,显示用户与餐厅距离的食品评论网站,还是其他任何需要使用地理坐标数据的应用程序,都无关紧要; 本教程将适用于任何此类用例。

That said, we’re going to use the example of a food reviews app that needs to show user’s the distance they are from a reviewed restaurant. So let’s do it!

也就是说,我们将使用食物评论应用程序的示例,该应用程序需要向用户显示他们与评论餐厅的距离。 让我们开始吧!

(Tutorial)

To start, you’re going to need an 8base Workspace. The free plan will work just fine for this tutorial, so go ahead and sign up at app.8base.com if you haven’t already.

首先,您将需要一个8base工作区。 免费计划对于本教程来说效果很好,因此,如果尚未注册,请继续在app.8base.com上注册。

Once logged into 8base, we’re going to want to set up a simple data model for our reviews. It’s going to look like this:

登录8base后,我们将要为我们的评论建立一个简单的数据模型。 它看起来像这样:

Restaurants

餐厅

  • name: Text名称:文字
  • location: Geo位置:地理位置
  • Reviews: has_many Reviews评论:has_many评论

Reviews

评论

  • content: Text内容:文字
  • rating: Number等级:数量
  • Restaurant: has_one Restaurants餐厅:has_one餐厅

导入数据模型和种子数据(Importing the Data Model and Seed Data)

You’re welcome to quickly construct this data model using the 8base data builder. However, I’d recommend importing the schema and seed data using the CLI. It will not only quickly set up the data model but also populate the database with 20 restaurants and 40 reviews.

欢迎您使用8base数据构建器快速构建此数据模型。 但是,我建议使用CLI导入架构和种子数据。 它不仅可以快速建立数据模型,而且可以在数据库中填充20个餐厅和40条评论。

The following commands will quickly get you up and running! You’ll want to feed the last import command a path to this file.

以下命令将快速使您启动并运行! 您需要向最后一个导入命令提供此文件的路径。

# Install the cli
npm install -g 8base-cli
# Authenticate the CLI
8base login
# Create an empty 8base project and select workspace
8base init rr-tutorial -e
# Import the schema
8base import -f [PATH_TO schema-and-data.json]
{% code-block-end%}

At this point, the data model should be set up and the seed data ready to use! You can hop back into the 8base console and look at the Data Builder/Viewer to see the imported schema and records. It will look like the screenshot below.

此时,应建立数据模型,并准备使用种子数据! 您可以跳回到8base控制台,并查看Data Builder / Viewer以查看导入的模式和记录。 它看起来像下面的截图。

geoserver java 发布pgsql geoserver geojson_git

8base data viewer with imported records 8base数据查看器与导入的记录

查看保存的位置数据(Reviewing the Saved Location Data)

If you look at the Restaurants table, you’ll see there is a field called location that has the type Geo selected with the format set to Point. Geo Points are simply a latitude and longitude coordinate that together references a specific point on the map.

如果查看“餐厅”表,您会看到一个名为location的字段,该字段的类型为Geo ,其格式设置为Point 。 地理点只是一个纬度和经度坐标,它们共同引用地图上的特定点。

Now switch over to the Data tab and you’ll see the Location column displaying all the latitude and longitude coordinates in arrays. This is how the API received the data, though when editing a record via the UI 8base separates the two values.

现在切换到“数据”选项卡,您将看到“位置”列显示数组中的所有纬度和经度坐标。 尽管通过UI 8base编辑记录时,两个值是分开的,但API就是这样接收数据的。

geoserver java 发布pgsql geoserver geojson_API_02

Editing latitude and longitude fields in 8base console

在8base控制台中编辑纬度和经度字段

With this GraphQL API being auto-generated by 8base, all our geo coordinate operations are ready to be used. So let’s pop over to the API Explorer to see what kind of queries and mutations we have access to.

通过8base自动生成的GraphQL API,我们所有的地理坐标操作都可以使用。 因此,让我们跳到API资源管理器以查看我们可以访问的查询和变异类型。

(Writing Queries with Geo Filtering)

There are many methods to get a user’s current location when they are using your app or website. For example, many modern browsers have implemented the Geolocation API that prompts the user for permission to access their current location. When granted, the following code snippet would return the user’s location using geo coordinates.

用户使用您的应用程序或网站时,有很多方法可以获取他们的当前位置。 例如,许多现代浏览器已经实现了Geolocation API,该API提示用户访问其当前位置的权限。 如果获得批准,则以下代码段将使用地理坐标返回用户的位置。

navigator.geolocation.getCurrentPosition(
 ({ coords: { latitude, longitude } }) => console.log([latitude, longitude])
)

Using the returned latitude and longitude, you’ll be able to pass the user’s location as an argument to your GraphQL query. We just happen to be working with a user’s current location in this example. Any valid geo coordinates could be passed to a query for use in filtering, depending on the use case.

使用返回的纬度和经度,您将能够将用户的位置作为参数传递给GraphQL查询。 在此示例中,我们恰好正在使用用户的当前位置。 根据使用情况,可以将任何有效的地理坐标传递给查询以进行过滤。

So, let’s go ahead and write a query that allows us the filter by the distance a Restaurant is from the user’s current location, using meters. We’re going to manually set the user’s current location to latitude 40.748981˚ longitude -73.985910˚, which is roughly the Empire State Building in New York City.

因此,让我们继续编写一个查询,该查询使我们可以使用仪表按照餐厅到用户当前位置的距离来过滤我们。 我们将手动将用户的当前位置设置为纬度40.748981˚经度-73.985910˚,这大约是纽约市的帝国大厦。

Spoiler Alert: The restaurant data is all of the places in Manhattan!

剧透提醒:餐厅数据是曼哈顿的所有地方!

In the API Explorer, open up the Variables input and paste in the following JSON blob. The top-level keys we’ll be able to use as arguments in the query editor.

在API Explorer中,打开“变量”输入,然后粘贴到以下JSON blob中。 我们将能够在查询编辑器中将其用作参数的顶级键。

{
  “distance”: 2500,
  “userLocation”: {
    “type”: “Point”,
    “coordinates”: [40.748981, -73.98591]
  }
}

Now we’ll write out our actual query. See it below and read the in code comments for context!

现在,我们将写出实际的查询。 在下面查看并阅读上下文中的代码注释!

query(
  # Set arguments with specified required types
  $userLocation: GeometryInput!
  $distance: Float!
) {
  # User restaurantsList query with filter
  restaurantsList(
    filter: {
      location: {
        # When filtering on a Geo type Field, we can use the Distance predicate.
        distance: {
          # Specify the users current location (argument format)
          from: $userLocation
          unit: meters
          value: {
            # Get results LESS THAN OR EQUAL TO (lte) the specified distance
            lte: $distance
          }
        }
      }
    }
  ) {
    count
    items {
      name
      location {
        coordinates
      }
    }
  }
}

Run the query and BOOM…we’re in business! Well, not exactly.

运行查询,BOOM…我们从事业务! 好吧,不完全是。

geoserver java 发布pgsql geoserver geojson_python_03

Filtering records by distance from a lat-long point

按距经纬点的距离过滤记录

We can see that from our given userLocation, 6 restaurants were within 2500 meters. However, we only filtered the list and in the response got back geo coordinates. What we need is to get back the actual distance calculation from our user!

ã从给定的用户位置可以看到2500米范围内有6家餐厅。 但是,我们仅过滤了列表,并且在响应中返回了地理坐标。 我们需要从用户那里获取实际的距离计算结果!

(Using ExtraFields to Return Distance Calculations)

We’ll be able to accomplish this using an API feature called extraFields. Essentially, what we’re going to do is return a calculated result that’s not a part of our core data model. This is what extraFields enables us to do.

我们将能够使用称为extraFields的API功能来完成此任务。 本质上,我们要做的是返回计算结果,该结果不属于我们的核心数据模型。 这就是extraFields使我们能够做到的。

Let’s re-work our query a bit to accomplish this requirement. Just like last time, see it below and read the in-code comments for context!

让我们重新整理一下查询以完成此要求。 与上次一样,请在下面查看并阅读上下文中的代码注释!

query(
  # Set the same arguments with specified required types
  $userLocation: GeometryInput!
  $distance: Float!
) {
  # User restaurantsList query with filter and extraFields
  restaurantsList(
    extraFields: {
      # Specify which field the "extra field" will be calculated from
      location: {
        # Give the new field a unique name
        as: "distanceFromUser"
        fn: {
          # Declare the function calculate that will be used. In this case, distance from the userLocation using meters.
          distance: { from: $userLocation, unit: meters }
        }
      }
    }
    filter: {
      # Instead of repeating ourselves in the filter, we can filter using the extraField that's getting calculated!
      _extraField: {
        # Specify the extraField we're filtering by.
        alias: "distanceFromUser"
        # Declare the field type and relevant predicate. In this case, a float less than the specified distance.
        float: { lt: $distance }
      }
    }
  ) {
    count
    items {
      name
      # extraFields can then be collected in the response like so.
      _extraFields {
        distanceFromUser: Float
      }
    }
  }
}

Pretty cool! Right? As you can see in your response, you’re now getting back the user’s precise distance from the different restaurant locations in meters.

太酷了! 对? 正如您在响应中看到的那样,您现在可以从米中的不同餐厅位置获得用户的精确距离。

Using extraFields distances with lat long points 使用经纬度长点的extraFields距离

向查询添加分组和聚合(Adding Grouping and Aggregation to the Query)

As you can imagine, the $distance can be adjustable by your user in the UI. For example, they may select 1km, 5km, or 10km as the distance while using the app and then the query would run and filter accordingly. That said, other types of data can get generated in conjunction with the geo-coordinate filters we just explored.

可以想象,用户可以在UI中调整$distance 。 例如,他们可以在使用应用程序时选择1km,5km或10km作为距离,然后查询将运行并进行相应的过滤。 就是说,其他类型的数据可以与我们刚刚探讨的地理坐标过滤器结合生成。

For example, what if the user not only wanted to see restaurants within 2500 meters but also wanted to see the average rating from reviews? How could that be accomplished?

例如,如果用户不仅想查看2500米以内的餐厅,还想查看评论的平均评分怎么办? 怎么能做到?

Well, by diving into the groubBy feature of the API we’d be able to accomplish this next level of complexity. Let’s take a look!

好吧,通过深入研究API的groubBy功能,我们将能够实现下一级别的复杂性。 让我们来看看!

query(
  # Set arguments with specified required types
  $userLocation: GeometryInput!
  $distance: Float!
) {
  # User restaurantsList query with filter
  restaurantsList(
    extraFields: {
      # Specify which field the "extra field" will be calculated from
      location: {
        # Give the new field a unique name
        as: "distanceFromUser"
        fn: {
          # Declare the function calculate that will be used. In this case, distance from the userLocation using meters.
          distance: { from: $userLocation, unit: meters }
        }
      }
    }
    filter: {
      # Instead of repeating ourselves in the filter, we can filter using the extraField that's getting calculated!
      _extraField: {
        # Specify the extraField we're filtering by.
        alias: "distanceFromUser"
        # Declare the field type and relevant predicate. In this case, a float less than the specified distance.
        float: { lt: $distance }
      }
    }
  ) {
    count
    items {
      name
      _extraFields {
        distanceFromUser: Float
      }
      # In the response we specify a groupBy argument to the relation
      reviews(
        groupBy: {
          query: {
            # On the ratings field, we aggregate an average of the reviews belonging to a restaurant.
            rating: { as: "avgRating", fn: { aggregate: AVG } }
            # We alias the grouping as reviews
            _group: { as: "reviews" }
          }
        }
      ) {
        groups {
          # In each group, we return the avgRating as a float
          avgRating: Float
          # We return the "items" (reviews) with content and the individual rating
          reviews: ReviewGroup {
            items {
              content
              rating
            }
          }
        }
      }
    }
  }
}

An example of the expected response is below.

预期响应的示例如下。

{
  "data": {
    "restaurantsList": {
      "count": 6,
      "items": [
        {
          "name": "The Burrito Box",
          "_extraFields": {
            "distanceFromUser": 2149.482589321746
          },
          "reviews": {
            "groups": [
              {
                "avgRating": 4,
                "reviews": {
                  "items": [
                    {
                      "content": "I liked it!",
                      "rating": 4
                    },
                    {
                      "content": "It was okay.",
                      "rating": 4
                    }
                  ]
                }
              }
            ]
          }
        },
        {
          "name": "Chick'nCone",
          "_extraFields": {
            "distanceFromUser": 2101.9531673470437
          },
          "reviews": {
            "groups": [
              {
                "avgRating": 4.5,
                "reviews": {
                  "items": [
                    {
                      "content": "It was okay.",
                      "rating": 4.5
                    },
                    {
                      "content": "I liked it!",
                      "rating": 4.5
                    }
                  ]
                }
              }
            ]
          }
        }
        // ... more!
      ]
    }
  }
}

(Final Thoughts)

As demonstrated, using an existing API platform we’re able to quickly standup an extremely powerful API that has advanced geo coordinate operations built into it. This can apply to a large variety of applications that you maybe building.

如所示,使用现有的API平台,我们能够快速建立一个功能强大的API,该API内置了高级地理坐标操作。 这可以应用于您可能要构建的各种应用程序。

I hope that you found this useful and interesting! Please let us know if you have any questions, as well as what projects you end up building using 8base!

希望您觉得这个有用和有趣! 如果您有任何疑问,请告诉我们,以及最终使用8base构建哪些项目!

Originally published at https://www.8base.com.

最初发布在https://www.8base.com上。

翻译自: https://medium.com/javascript-in-plain-english/building-map-interface-apps-using-a-geo-json-graphql-api-55b2f802d517

geo.json网站