Skip to content

Commit

Permalink
added ./places/builings endpoint for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
AdenForshaw committed Nov 10, 2024
1 parent 71c451e commit 2844c5f
Show file tree
Hide file tree
Showing 24 changed files with 2,816 additions and 266 deletions.
2,320 changes: 2,205 additions & 115 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "overture-maps-api",
"version": "0.1.0",
"version": "0.1.1",
"description": "",
"author": "",
"private": true,
Expand All @@ -27,9 +27,12 @@
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^8.0.1",
"@turf/turf": "^7.1.0",
"@types/geojson": "^7946.0.14",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"dotenv": "^16.4.5",
"geojson": "^0.5.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.1",
Expand Down
64 changes: 51 additions & 13 deletions src/bigquery/bigquery.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,57 @@ export class BigQueryService {

private parseBuildingRow(row: any): Building {

this.logger.log(row);
return {

id: row.id,
geometry: parsePolygonToGeoJSON(row.geometry),
geometry: parsePolygonToGeoJSON(row.geometry.value),
bbox: {
xmin: parseFloat(row.bbox.xmin),
xmax: parseFloat(row.bbox.xmax),
ymin: parseFloat(row.bbox.ymin),
ymax: parseFloat(row.bbox.ymax),
},
version: row.version,
sources: row.sources.list.map((source: any) => ({
property: source.element.property,
dataset: source.element.dataset,
record_id: source.element.record_id,
update_time: source.element.update_time,
confidence: source.element.confidence ? parseFloat(source.element.confidence) : null,
})),

subtype: row.subtype,
class: row.class,
names: row.names,
level: row.level,
has_parts: row.has_parts,
height: row.height,
is_underground: row.is_underground,
num_floors: row.num_floors,
num_floors_underground: row.num_floors_underground,
min_height: row.min_height,
min_floor: row.min_floor,
facade_color: row.facade_color,
facade_material: row.facade_material,
roof_material: row.roof_material,
roof_shape: row.roof_shape,
roof_direction: row.roof_direction,
roof_orientation: row.roof_orientation,
roof_color: row.roof_color,
roof_height: row.roof_height,
ext_distance: parseFloat(row.ext_distance),
theme: row.theme,
type: row.type,


}
}

private parsePlaceRow(row: any): Place {

return {
id: row.id,
geometry: parsePointToGeoJSON(row.geometry),
geometry: parsePointToGeoJSON(row.geometry.value),
bbox: {
xmin: parseFloat(row.bbox.xmin),
xmax: parseFloat(row.bbox.xmax),
Expand Down Expand Up @@ -88,7 +127,7 @@ export class BigQueryService {
region: address.element?.region,
country: address.element?.country,
})) : [],
distance_m: parseFloat(row.distance_m),
ext_distance: parseFloat(row.ext_distance),
}
}

Expand Down Expand Up @@ -192,7 +231,7 @@ export class BigQueryService {
longitude: number,
radius: number = 1000,
limit?: number
): Promise<Place[]> {
): Promise<Building[]> {

let queryParts: string[] = [];

Expand All @@ -217,14 +256,14 @@ SET search_area_geometry = (
-- Step 2: Select buildings within the search area
SELECT
*
,ST_Distance(geometry, ST_GeogPoint(${longitude}, ${latitude})) AS ext_distance
FROM
\`bigquery-public-data.overture_maps.building\` AS s
WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_GeogPoint(${longitude}, ${latitude}), 1000)
WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_GeogPoint(${longitude}, ${latitude}), ${radius})`);

`)
// Order by distance if latitude and longitude are provided
if (latitude && longitude) {
queryParts.push(`ORDER BY distance_m`);
queryParts.push(`ORDER BY ext_distance`);
}

// Limit results if no filters are provided
Expand All @@ -237,7 +276,7 @@ WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_Ge
this.logger.debug(`Running query: ${query}`);

const { rows } = await this.runQuery(query);
return rows.map((row: any) => this.parsePlaceRow(row));
return rows.map((row: any) => this.parseBuildingRow(row));
}


Expand All @@ -260,7 +299,7 @@ WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_Ge
queryParts.push(`SELECT *`);

if (latitude && longitude) {
queryParts.push(`, ST_Distance(geometry, ST_GeogPoint(${longitude}, ${latitude})) AS distance_m`);
queryParts.push(`, ST_Distance(geometry, ST_GeogPoint(${longitude}, ${latitude})) AS ext_distance`);
}

queryParts.push(`FROM \`${SOURCE_DATASET}.place\``);
Expand All @@ -284,7 +323,6 @@ WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_Ge
}

if(categories && categories.length > 0){
console.log(categories);
whereClauses.push(`categories.primary IN UNNEST(["${categories.join('","')}"])`);
}

Expand All @@ -299,7 +337,7 @@ WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_Ge

// Order by distance if latitude and longitude are provided
if (latitude && longitude) {
queryParts.push(`ORDER BY distance_m`);
queryParts.push(`ORDER BY ext_distance`);
}

// Limit results if no filters are provided
Expand Down Expand Up @@ -355,7 +393,7 @@ WHERE ST_WITHIN(s.geometry, search_area_geometry) and ST_DWithin(geometry, ST_Ge
billedAmountInGB: Math.round(totalBytesBilled / 1000000000),
bytesProcessedInGB: Math.round(totalBytesProcessed / 1000000000),
durationMs: Date.now() - start,
costInUSD: Math.round(totalBytesBilled / 1000000000) * 5
costInUSD: (Math.round(totalBytesBilled / 1000000000) * 5)/100
}

const QueryFirstLine = query.split('\n')[0];
Expand Down
35 changes: 32 additions & 3 deletions src/buildings/buildings.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,36 @@
https://docs.nestjs.com/controllers#controllers
*/

import { Controller } from '@nestjs/common';
import { Controller, Logger, Query,Get } from '@nestjs/common';
import { BuildingsService } from './buildings.service';
import { GetBuildingsQuery } from './dto/requests/get-buildings-query.dto';
import { BuildingDto, toBuildingDto } from './dto/responses/building-response.dto';
import { wrapAsGeoJSON } from '../utils/geojson';
import { Format } from '../common/dto/requests/get-by-location.dto';

@Controller()
export class BuildingsController { }

@Controller('buildings')
export class BuildingsController {

logger = new Logger('BuildingsController');

constructor(
private readonly buildingsService: BuildingsService,
) {}

@Get()
async getBuildings(@Query() query: GetBuildingsQuery): Promise<BuildingDto[]|any> {


const buildings = await this.buildingsService.getBuildings(query);

const dtoResults = buildings.map((building: any) => toBuildingDto(building, query));

if(query.format === Format.GEOJSON) {
return wrapAsGeoJSON(dtoResults)
}else{
return dtoResults
}
}

}
8 changes: 4 additions & 4 deletions src/buildings/buildings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AuthedUser, User } from '../decorators/authed-user.decorator';
import { ValidateLatLngUser } from '../decorators/validate-lat-lng-user.decorator';
import { ConfigService } from '@nestjs/config';
import { CloudStorageCacheService } from '../cloudstorage-cache/cloudstorage-cache.service';
import { GetBuildingsQuery } from './dto/get-buildings-query.dto';
import { GetBuildingsQuery } from './dto/requests/get-buildings-query.dto';
import { Building } from './interfaces/building.interface';

@Injectable()
Expand All @@ -25,16 +25,16 @@ export class BuildingsService {
) {}

async getBuildings(query: GetBuildingsQuery): Promise<Building[]> {
const { lat, lng, radius } = query;
const { lat, lng, radius,limit } = query;

// Check if cached results exist in GCS
// Check if cached results exist in File Cache
const cacheKey = `get-places-brands-${JSON.stringify(query)}`;
const cachedResult = await this.cloudStorageCache.getJSON(cacheKey);
if (cachedResult) {
return cachedResult;
}

const results = await this.bigQueryService.getBuildingsNearby( lat, lng, radius,1);
const results = await this.bigQueryService.getBuildingsNearby( lat, lng, radius,limit);
await this.cloudStorageCache.storeJSON (results,cacheKey);
return results;//
}
Expand Down
35 changes: 0 additions & 35 deletions src/buildings/dto/get-buildings-query.dto.ts

This file was deleted.

9 changes: 9 additions & 0 deletions src/buildings/dto/requests/get-buildings-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsNumber, IsOptional, IsString, MaxLength, Min, MinLength, ValidateIf } from 'class-validator';
import { GetByLocationDto } from '../../../common/dto/requests/get-by-location.dto';

export class GetBuildingsQuery extends GetByLocationDto {


}
60 changes: 60 additions & 0 deletions src/buildings/dto/responses/building-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { GeometryDto } from '../../../common/dto/responses/geometry.dto';
import { Building } from '../../interfaces/building.interface';
import { GetByLocationDto } from 'src/common/dto/requests/get-by-location.dto';
import { Geometry } from 'geojson';

export class BuildingPropertiesDto {

}

export class BuildingDto {
@ApiProperty({ description: 'Unique identifier of the place.', example: '12345' })
id: string;

@ApiProperty({ description: 'Type of place or feature.', example: 'Point of Interest' })
type: string;

@ApiProperty({
description: 'Geometric representation of the place.',
type: () => GeometryDto,
})
geometry: Geometry;

@ApiProperty({
description: 'Properties and additional details.',
type: () => BuildingPropertiesDto,
})
properties: BuildingPropertiesDto;

constructor(data: Building) {
this.id = data.id;
this.geometry = data.geometry;
if(!this.properties) this.properties = new BuildingPropertiesDto();
//Object.assign(this, data);
}
}

export const toBuildingDto = (data , requestQuery:GetByLocationDto) => {

const excludeFieldsFromProperties = ['properties','geometry','ext_distance','bbox'];
const properties = {...data};
excludeFieldsFromProperties.forEach(field => delete properties[field]);

const rPlace = new BuildingDto(data)
rPlace.properties = properties;
rPlace.geometry = data.geometry;

//remove any fields that are not requested
if(requestQuery.includes && requestQuery.includes.length > 0) {
const filteredProperties = {};
requestQuery.includes.forEach((field) => {

if(rPlace.properties[field]) {
filteredProperties[field] = rPlace.properties[field];
}
});
rPlace.properties = filteredProperties;
}
return rPlace
}
30 changes: 30 additions & 0 deletions src/buildings/interfaces/building.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
import { Source } from "../../places/interfaces/place.interface";
import { Bbox } from "../../common/interfaces/geometry.interface";
import { Geometry, Polygon } from "geojson";


export interface Building {

id: string;
geometry: Polygon;
bbox?: Bbox;
version: string;
sources: Source[];
subtype: string;
class: string;
names: string;
level: string;
has_parts: boolean;
height: number;
is_underground: boolean;
num_floors: number;
num_floors_underground: number;
min_height: number;
min_floor: number;
facade_color: string;
facade_material: string;
roof_material: string;
roof_shape: string;
roof_direction: string;
roof_orientation: string;
roof_color: string;
roof_height: number;
ext_distance: number;
theme: string;
type: string;
}
Loading

0 comments on commit 2844c5f

Please sign in to comment.