Beakyn Buildings Service

Introduction

A. How the POI taxonomy relates to buildings, cities and neighorhoods

Understanding the relationship or intersection between POIs and Buildings facilitates a our understanding of a building. This logic applies to neighorhoods and cities as well.

  • The profile of a POI is the categorization and classification of the POI using a taxonomy.
  • The profile of a building is the sum of the profile of the POIs in that building.
  • The profile of a neighborhood is the sum of the profile of the POIs in that neighborhood.
  • The profile of a city is the sum of the profile of the POIs in that city.

B. Request and response logic

Responses will be always in geoJSON format and will return as FeatureCollection with Features of type GeometryCollection, which include a polygon or multipolygon at index 0 and a point at index 1.

If a single building is requested using an ID the response will be a geoJSON of type Feature.

Also a property centroid is included on the root of the object. This is the center of mass for a polygon. It is the same value as in GeometryCollection index 1.

The centroid is used to calculate the radius included on the properties.

A centroid and a building polygon can work together with appplications to detect user pivots.

Other properties include:

  • radius - Minimum radius calculated from the centroid (used to establish a radial geofence around a building)
  • building - Building type inherited from Open Street Maps
  • countryCode - Example: "USA"
  • country - Example: "United States"
  • regionCode - Example: "NY"
  • region - Example: "New York"
  • locality - Example: "New York"
  • osmId - Open Street Maps id

List response example:

{
  "type": "FeatureCollection",
  "total": 1,
  "features": [
    {
      "id": "57d41c2f8a5f72bd70323591",
      "properties": {
        "radius": 32,
        "building": "yes",
        "countryCode": "USA",
        "country": "United States",
        "regionCode": "NY",
        "region": "New York",
        "locality": "New York",
        "osmId": 248773769
      },
      "type": "Feature",
      "geometry": {
        "geometries": [
          {
            "coordinates": [
              [
                [-73.99225590000002,40.736458500000005],
                [-73.9920985,40.7366774],
                [-73.9920584,40.7366608],
                [-73.9919515,40.73681090000001],
                [-73.99188400000001,40.736904800000005],
                [-73.99187990000001,40.73691050000001],
                [-73.99171750000001,40.736843],
                [-73.99192270000002,40.7365574],
                [-73.99201619999998,40.736589300000006],
                [-73.9920758,40.7365075],
                [-73.9920182,40.7364972],
                [-73.9920491,40.73641580000001],
                [-73.99225590000002,40.736458500000005]
              ]
            ],
            "type": "Polygon"
          },
          {
            "coordinates": [-73.991993975,40.73665275833334],
            "type": "Point"
          }
        ],
        "type": "GeometryCollection"
      },
      "centroid": {
        "lon": -73.991993975,
        "lat": 40.73665275833334
      }
    }
  ]
}

NOTE: All list requests will include the total of results at the root of the object.

Single building response example:

{
  "id": "57d41c2f8a5f72bd70323591",
  "properties": {
    "radius": 32,
    "building": "yes",
    "countryCode": "USA",
    "country": "United States",
    "regionCode": "NY",
    "region": "New York",
    "locality": "New York",
    "osmId": 248773769
  },
  "type": "Feature",
  "geometry": {
    "geometries": [
      {
        "coordinates": [
          [
            [-73.99225590000002,40.736458500000005],
            [-73.9920985,40.7366774],
            [-73.9920584,40.7366608],
            [-73.9919515,40.73681090000001],
            [-73.99188400000001,40.736904800000005],
            [-73.99187990000001,40.73691050000001],
            [-73.99171750000001,40.736843],
            [-73.99192270000002,40.7365574],
            [-73.99201619999998,40.736589300000006],
            [-73.9920758,40.7365075],
            [-73.9920182,40.7364972],
            [-73.9920491,40.73641580000001],
            [-73.99225590000002,40.736458500000005]
          ]
        ],
        "type": "Polygon"
      },
      {
        "coordinates": [-73.991993975,40.73665275833334],
        "type": "Point"
      }
    ],
    "type": "GeometryCollection"
  },
  "centroid": {
    "lon": -73.991993975,
    "lat": 40.73665275833334
  }
}

C. The Point-in-Polygon API

This API will find buildings using the coordinates system: [lon,lat].

It is possible to find more than one building that intersects the same POI since nested polygons could exists. That's why this API will always return a FeatureCollection.

GET /v1/buildings?point-in-polygon=[lon,lat]

Parameter Description
point-in-polygon Array: [longitude,latitude] It should be Numbers on the order presented.

NOTE: The order of values for the coordinates is important. It follows the geoJSON standatd [longitude,latitude]


D. The Near API

This API will find buildings using the coordinates system: [lon,lat].

The near API will return buildings on a FeatureCollection sorted by distance from the point passed in the query.

GET /v1/buildings?near=[lon,lat]&max-distance=500&min-distance=10

Parameters:

Parameter Description
near Array: [longitude,latitude] It should be Numbers on the order presented.
max-distance Number : Max radius distance in meters
min-distance Number : Min radius distance in meters

NOTE: The order of values for the coordinates is important. It follows geoJSON standatd [longitude,latitude]


Not implemented yet.

E. The Buildings API

The Buildings API supports a scenario where we have a collection of POIs IDs and we want to retrieve the buildings for each one.

We could submit a request like this:

GET /v1/building?pois={poiId},{poiId},{poiId}

Parameter Description
pois list of Strings representing POI ids

How it works on the backend

The buildings microservice has a POI model that stores relationships between POIs and Buildings.

First, we query the POI collection to get all of the relevant building IDs:

const poiIds = JSON.parse(query.pois);

Poi.find({
    _id: { $in: poiIds.map((o) => mongoose.Types.ObjectId(o)) }
})
    .then((response) => {
        // Convert to ObjectId of store as string.
        const buildingIds =
            response.map((o) => mongoose.Types.ObjectId(o.buildingId));

        return Building.find({
            _id: { $in: buildingIds },
        });
    });

This will return an array with POI IDs and Building IDs:

[
    {
        "_id": "57d3e5ca0f5fce9114bdd94a",
        "buildingId": "57d3e5ca0f5fce9114bdd94a"
    },
    {
        "_id": "57d3e5ca0f5fce9114bdd94b",
        "buildingId": "57d3e5ca0f5fce9114bdd94a"
    },
    {
        "_id": "57d3e5ca0f5fce9114bdd94c",
        "buildingId": "57d3e5ca0f5fce9114bdd94a"
    }
]

Then, on a second query to MongoDB, we can retrive all the Buildings using the IDs.

This requires two queries to MongoDB making the request more complex and slower. But now the relationship is completly decoupled from the original POI service.


Not implemented yet.

F. The POI API

We can always use the point-in-polygon parameter to find a Building from a [lon,lat] coordinate array. But what if we want to find the building of a POI already registerd with a relationship?

In that case we could use the endpoint above just passing one POI ID, or we could request direclty to the POIs/Buildings endpoint.

GET /v1/pois/{poiId}/buildings

This should make it a little faster since we're looking for a specific ID instead of a collection.

How it works on the backend

const poiId = param.poiId

Poi.find({
    _id: ObjectId(poiId),
})
    // Don't need to use ObjectId() if it's already store like that.
    .then((response) => Building.find({ _id: response.buildingId }))

Not implemented yet.

G. Get all POI IDs inside a Building

If we want to return all the POI IDs that exist inside of a Building, then we could pass the following request to the Building/POI endpoint.

GET /v1/buildings/{buildingId}/pois

NOTE: This will only return POI IDs. Later, these IDs can be used to query the right database to return the nessesary properties.

How it works on the backend

Poi.find({
    buildingId: ObjectId('57d3e5ca0f5fce9114bdd94a'),
});

Not implemented yet.

H. Create a POI/Building relationship

The remote POI service is responsible to create, update and delete relationships with buildings.

To create a relationship a curator could execute the following request after saving a POI:

POST /v1/pois/

Request payload:

{
    id: {poiId},
    location: [lon, lat]
}

Response:

{
    id: {poiId},
    buildingId: {buildingId}
}

If no building is found, then the request will return a 404 error.


Not implemented yet.

I. Updating a POI/Building relationship

If for some reason the location of the POI changes with new coordinates, then a PUT request can be made to find and update the Building ID.

PUT /v1/pois/{poiId}

Request payload:

{
    location: [lon, lat]
}

Response:

{
    id: {poiId},
    buildingId: {buildingId}
}

Not implemented yet.

J. Delete a POI/Building relationship

The POI remote serivice is responsible for mantaining the relationships between POI and Building healthy. If a POI is destrooyed a workerd could execute the following request to clean the relationship on the buildings service.

DELETE /v1/pois/{poiId}

Response:

204 No Content