Skip to content

Commit

Permalink
implement endpoint for project tiles in comm district
Browse files Browse the repository at this point in the history
implement findCapitalProjectTilesByBoroughIdCommunityDistrictId

closes #364
  • Loading branch information
TangoYankee committed Aug 8, 2024
1 parent 259e199 commit 20050c2
Show file tree
Hide file tree
Showing 13 changed files with 374 additions and 3 deletions.
2 changes: 1 addition & 1 deletion openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ paths:
$ref: "#/components/responses/InternalServerError"
/boroughs/{boroughId}/community-districts/{communityDistrictId}/capital-projects/{z}/{x}/{y}.pbf:
get:
summary: 🚧 Mapbox Vector Tiles for capital projects intersecting a community district
summary: Mapbox Vector Tiles for capital projects intersecting a community district
operationId: findCapitalProjectTilesByBoroughIdCommunityDistrictId
tags:
- MVT
Expand Down
25 changes: 25 additions & 0 deletions src/borough/borough.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import {
Injectable,
Param,
Query,
Res,
UseFilters,
UsePipes,
} from "@nestjs/common";
import { BoroughService } from "./borough.service";
import { Response } from "express";
import {
FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams,
FindCapitalProjectsByBoroughIdCommunityDistrictIdPathParams,
FindCapitalProjectsByBoroughIdCommunityDistrictIdQueryParams,
FindCommunityDistrictGeoJsonByBoroughIdCommunityDistrictIdPathParams,
FindCommunityDistrictsByBoroughIdPathParams,
findCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParamsSchema,
findCapitalProjectsByBoroughIdCommunityDistrictIdPathParamsSchema,
findCapitalProjectsByBoroughIdCommunityDistrictIdQueryParamsSchema,
findCommunityDistrictGeoJsonByBoroughIdCommunityDistrictIdPathParamsSchema,
Expand Down Expand Up @@ -86,4 +90,25 @@ export class BoroughController {
{ ...pathParams, ...queryParams },
);
}

@UsePipes(
new ZodTransformPipe(
findCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParamsSchema,
),
)
@Get(
"/:boroughId/community-districts/:communityDistrictId/capital-projects/:z/:x/:y.pbf",
)
async findCapitalProjectTilesByBoroughIdCommunityDistrictId(
@Param()
params: FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams,
@Res() res: Response,
) {
const tiles =
await this.boroughService.findCapitalProjectTilesByBoroughIdCommunityDistrictId(
params,
);
res.set("Content-Type", "application/x-protobuf");
res.send(tiles);
}
}
8 changes: 8 additions & 0 deletions src/borough/borough.repository.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
boroughEntitySchema,
communityDistrictEntitySchema,
MultiPolygonSchema,
mvtEntitySchema,
} from "src/schema";
import { z } from "zod";

Expand Down Expand Up @@ -47,3 +48,10 @@ export const findCapitalProjectsByBoroughIdCommunityDistrictIdRepoSchema =
export type FindCapitalProjectsByBoroughIdCommunityDistrictIdRepo = z.infer<
typeof findCapitalProjectsByBoroughIdCommunityDistrictIdRepoSchema
>;

export const findCapitalProjectTilesByBoroughIdCommunityDistrictIdRepoSchema =
mvtEntitySchema;

export type FindCapitalProjectTilesByBoroughIdCommunityDistrictIdRepo = z.infer<
typeof findCapitalProjectTilesByBoroughIdCommunityDistrictIdRepoSchema
>;
71 changes: 69 additions & 2 deletions src/borough/borough.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import {
FindCommunityDistrictsByBoroughIdRepo,
FindCapitalProjectsByBoroughIdCommunityDistrictIdRepo,
FindCommunityDistrictGeoJsonByBoroughIdCommunityDistrictIdRepo,
FindCapitalProjectTilesByBoroughIdCommunityDistrictIdRepo,
} from "./borough.repository.schema";
import { capitalProject, communityDistrict } from "src/schema";
import { eq, sql, and } from "drizzle-orm";
import { FindCommunityDistrictGeoJsonByBoroughIdCommunityDistrictIdPathParams } from "src/gen";
import { eq, sql, and, isNotNull } from "drizzle-orm";
import {
FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams,
FindCommunityDistrictGeoJsonByBoroughIdCommunityDistrictIdPathParams,
} from "src/gen";

export class BoroughRepository {
constructor(
Expand Down Expand Up @@ -132,4 +136,67 @@ export class BoroughRepository {
throw new DataRetrievalException();
}
}

async findCapitalProjectTilesByBoroughIdCommunityDistrictId({
boroughId,
communityDistrictId,
z,
x,
y,
}: FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams): Promise<FindCapitalProjectTilesByBoroughIdCommunityDistrictIdRepo> {
try {
const tile = this.db
.select({
managingCodeCapitalProjectId:
sql<string>`${capitalProject.managingCode} || ${capitalProject.id}`.as(
`managingCodeCapitalProjectId`,
),
managingAgency: sql`${capitalProject.managingAgency}`.as(
`managingAgency`,
),
geom: sql<string>`
CASE
WHEN ${capitalProject.mercatorFillMPoly} && ST_TileEnvelope(${z},${x},${y})
THEN ST_AsMVTGeom(
${capitalProject.mercatorFillMPoly},
ST_TileEnvelope(${z},${x},${y}),
4096,
64,
true
)
WHEN ${capitalProject.mercatorFillMPnt} && ST_TileEnvelope(${z},${x},${y})
THEN ST_AsMVTGeom(
${capitalProject.mercatorFillMPnt},
ST_TileEnvelope(${z},${x},${y}),
4096,
64,
true
)
END`.as("geom"),
})
.from(capitalProject)
.leftJoin(
communityDistrict,
sql`
ST_Intersects(${communityDistrict.mercatorFill}, ${capitalProject.mercatorFillMPoly})
OR ST_Intersects(${communityDistrict.mercatorFill}, ${capitalProject.mercatorFillMPnt})`,
)
.where(
and(
eq(communityDistrict.id, communityDistrictId),
eq(communityDistrict.boroughId, boroughId),
),
)
.as("tile");
const data = await this.db
.select({
mvt: sql<Buffer>`ST_AsMVT(tile, 'capital-project-fill', 4096, 'geom')`,
})
.from(tile)
.where(isNotNull(tile.geom));
return data[0].mvt;
} catch {
throw new DataRetrievalException();
}
}
}
21 changes: 21 additions & 0 deletions src/borough/borough.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Test } from "@nestjs/testing";
import {
findBoroughsQueryResponseSchema,
findCapitalProjectsByBoroughIdCommunityDistrictIdQueryResponseSchema,
findCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponseSchema,
findCommunityDistrictGeoJsonByBoroughIdCommunityDistrictIdQueryResponseSchema,
findCommunityDistrictsByBoroughIdQueryResponseSchema,
} from "src/gen";
Expand Down Expand Up @@ -153,4 +154,24 @@ describe("Borough service unit", () => {
expect(parsedBody.order).toBe("managingCode, capitalProjectId");
});
});

describe("findCapitalProjectTilesByBoroughIdCommunityDistrictId", () => {
it("should return an mvt when requesting coordinates", async () => {
const mvt =
await boroughService.findCapitalProjectTilesByBoroughIdCommunityDistrictId(
{
boroughId: "1",
communityDistrictId: "01",
z: 1,
x: 1,
y: 1,
},
);
expect(() =>
findCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponseSchema.parse(
mvt,
),
).not.toThrow();
});
});
});
9 changes: 9 additions & 0 deletions src/borough/borough.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ResourceNotFoundException } from "src/exception";
import {
FindCapitalProjectsByBoroughIdCommunityDistrictIdPathParams,
FindCapitalProjectsByBoroughIdCommunityDistrictIdQueryParams,
FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams,
} from "src/gen";
import { produce } from "immer";
import { CommunityDistrictGeoJsonEntity } from "./borough.repository.schema";
Expand Down Expand Up @@ -100,4 +101,12 @@ export class BoroughService {
capitalProjects,
};
}

async findCapitalProjectTilesByBoroughIdCommunityDistrictId(
params: FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams,
) {
return this.boroughRepository.findCapitalProjectTilesByBoroughIdCommunityDistrictId(
params,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Error } from "./Error";

export type FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams = {
/**
* @description A single character numeric string containing the common number used to refer to the borough. Possible values are 1-5.
* @type string
*/
boroughId: string;
/**
* @description The two character numeric string containing the number used to refer to the community district.
* @type string
*/
communityDistrictId: string;
/**
* @description viewport zoom component
* @type integer
*/
z: number;
/**
* @description viewport x component
* @type integer
*/
x: number;
/**
* @description viewport y component
* @type integer
*/
y: number;
};
/**
* @description A protobuf file formatted as Mapbox Vector Tile
*/
export type FindCapitalProjectTilesByBoroughIdCommunityDistrictId200 = string;
/**
* @description Invalid client request
*/
export type FindCapitalProjectTilesByBoroughIdCommunityDistrictId400 = Error;
/**
* @description Server side error
*/
export type FindCapitalProjectTilesByBoroughIdCommunityDistrictId500 = Error;
/**
* @description A protobuf file formatted as Mapbox Vector Tile
*/
export type FindCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponse =
string;
export type FindCapitalProjectTilesByBoroughIdCommunityDistrictIdQuery = {
Response: FindCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponse;
PathParams: FindCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParams;
Errors:
| FindCapitalProjectTilesByBoroughIdCommunityDistrictId400
| FindCapitalProjectTilesByBoroughIdCommunityDistrictId500;
};
1 change: 1 addition & 0 deletions src/gen/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./FindCapitalCommitmentsByManagingCodeCapitalProjectId";
export * from "./FindCapitalProjectByManagingCodeCapitalProjectId";
export * from "./FindCapitalProjectGeoJsonByManagingCodeCapitalProjectId";
export * from "./FindCapitalProjectTiles";
export * from "./FindCapitalProjectTilesByBoroughIdCommunityDistrictId";
export * from "./FindCapitalProjectsByBoroughIdCommunityDistrictId";
export * from "./FindCapitalProjectsByCityCouncilId";
export * from "./FindCityCouncilDistrictGeoJsonByCityCouncilDistrictId";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { z } from "zod";
import { errorSchema } from "./errorSchema";

export const findCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParamsSchema =
z.object({
boroughId: z.coerce
.string()
.regex(new RegExp("^([0-9]{1})$"))
.describe(
"A single character numeric string containing the common number used to refer to the borough. Possible values are 1-5.",
),
communityDistrictId: z.coerce
.string()
.regex(new RegExp("^([0-9]{2})$"))
.describe(
"The two character numeric string containing the number used to refer to the community district.",
),
z: z.coerce.number().describe("viewport zoom component"),
x: z.coerce.number().describe("viewport x component"),
y: z.coerce.number().describe("viewport y component"),
});
/**
* @description A protobuf file formatted as Mapbox Vector Tile
*/
export const findCapitalProjectTilesByBoroughIdCommunityDistrictId200Schema =
z.coerce.string();
/**
* @description Invalid client request
*/
export const findCapitalProjectTilesByBoroughIdCommunityDistrictId400Schema =
z.lazy(() => errorSchema);
/**
* @description Server side error
*/
export const findCapitalProjectTilesByBoroughIdCommunityDistrictId500Schema =
z.lazy(() => errorSchema);
/**
* @description A protobuf file formatted as Mapbox Vector Tile
*/
export const findCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponseSchema =
z.coerce.string();
1 change: 1 addition & 0 deletions src/gen/zod/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from "./findCapitalCommitmentTypesSchema";
export * from "./findCapitalCommitmentsByManagingCodeCapitalProjectIdSchema";
export * from "./findCapitalProjectByManagingCodeCapitalProjectIdSchema";
export * from "./findCapitalProjectGeoJsonByManagingCodeCapitalProjectIdSchema";
export * from "./findCapitalProjectTilesByBoroughIdCommunityDistrictIdSchema";
export * from "./findCapitalProjectTilesSchema";
export * from "./findCapitalProjectsByBoroughIdCommunityDistrictIdSchema";
export * from "./findCapitalProjectsByCityCouncilIdSchema";
Expand Down
29 changes: 29 additions & 0 deletions src/gen/zod/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ import {
findCapitalProjectsByBoroughIdCommunityDistrictIdPathParamsSchema,
findCapitalProjectsByBoroughIdCommunityDistrictIdQueryParamsSchema,
} from "./findCapitalProjectsByBoroughIdCommunityDistrictIdSchema";
import {
findCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponseSchema,
findCapitalProjectTilesByBoroughIdCommunityDistrictId400Schema,
findCapitalProjectTilesByBoroughIdCommunityDistrictId500Schema,
findCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParamsSchema,
} from "./findCapitalProjectTilesByBoroughIdCommunityDistrictIdSchema";
import {
findCapitalCommitmentTypesQueryResponseSchema,
findCapitalCommitmentTypes400Schema,
Expand Down Expand Up @@ -264,6 +270,25 @@ export const operations = {
500: findCapitalProjectsByBoroughIdCommunityDistrictId500Schema,
},
},
findCapitalProjectTilesByBoroughIdCommunityDistrictId: {
request: undefined,
parameters: {
path: findCapitalProjectTilesByBoroughIdCommunityDistrictIdPathParamsSchema,
query: undefined,
header: undefined,
},
responses: {
200: findCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponseSchema,
400: findCapitalProjectTilesByBoroughIdCommunityDistrictId400Schema,
500: findCapitalProjectTilesByBoroughIdCommunityDistrictId500Schema,
default:
findCapitalProjectTilesByBoroughIdCommunityDistrictIdQueryResponseSchema,
},
errors: {
400: findCapitalProjectTilesByBoroughIdCommunityDistrictId400Schema,
500: findCapitalProjectTilesByBoroughIdCommunityDistrictId500Schema,
},
},
findCapitalCommitmentTypes: {
request: undefined,
parameters: {
Expand Down Expand Up @@ -691,6 +716,10 @@ export const paths = {
{
get: operations["findCapitalProjectsByBoroughIdCommunityDistrictId"],
},
"/boroughs/{boroughId}/community-districts/{communityDistrictId}/capital-projects/{z}/{x}/{y}.pbf":
{
get: operations["findCapitalProjectTilesByBoroughIdCommunityDistrictId"],
},
"/capital-commitment-types": {
get: operations["findCapitalCommitmentTypes"],
},
Expand Down
Loading

0 comments on commit 20050c2

Please sign in to comment.