Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

365/cp tiles ccd #367

Merged
merged 2 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,26 @@ paths:
$ref: "#/components/responses/NotFound"
'500':
$ref: '#/components/responses/InternalServerError'
/city-council-districts/{cityCouncilDistrictId}/capital-projects/{z}/{x}/{y}.pbf:
get:
summary: Mapbox Vector Tiles for capital projects intersecting a city council district
operationId: findCapitalProjectTilesByCityCouncilDistrictId
tags:
- MVT
- Capital Projects
- City Council Districts
parameters:
- $ref: "#/components/parameters/cityCouncilDistrictIdParam"
- $ref: '#/components/parameters/viewportZoomParam'
- $ref: '#/components/parameters/viewportXParam'
- $ref: '#/components/parameters/viewportYParam'
responses:
'200':
$ref: '#/components/responses/MVT'
'400':
$ref: '#/components/responses/BadRequest'
'500':
$ref: '#/components/responses/InternalServerError'
/city-council-districts/{cityCouncilDistrictId}/capital-projects:
get:
summary: Find paginated capital projects within a specific city council district.
Expand Down
21 changes: 21 additions & 0 deletions src/city-council-district/city-council-district.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
NotFoundExceptionFilter,
} from "src/filter";
import {
FindCapitalProjectTilesByCityCouncilDistrictIdPathParams,
findCapitalProjectTilesByCityCouncilDistrictIdPathParamsSchema,
FindCityCouncilDistrictGeoJsonByCityCouncilDistrictIdPathParams,
findCityCouncilDistrictGeoJsonByCityCouncilDistrictIdPathParamsSchema,
FindCityCouncilDistrictTilesPathParams,
Expand Down Expand Up @@ -68,6 +70,25 @@ export class CityCouncilDistrictController {
return this.cityCouncilDistrictService.findGeoJsonById(params);
}

@UsePipes(
new ZodTransformPipe(
findCapitalProjectTilesByCityCouncilDistrictIdPathParamsSchema,
),
)
@Get("/:cityCouncilDistrictId/capital-projects/:z/:x/:y.pbf")
async findCapitalProjectTilesCityCouncilDistrictId(
@Param()
params: FindCapitalProjectTilesByCityCouncilDistrictIdPathParams,
@Res() res: Response,
) {
const tiles =
await this.cityCouncilDistrictService.findCapitalProjectTilesByCityCouncilDistrictId(
params,
);
res.set("Content-Type", "application/x-protobuf");
res.send(tiles);
}

@UsePipes(
new ZodTransformPipe(
findCapitalProjectsByCityCouncilIdPathParamsSchema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export const findTilesRepoSchema = mvtEntitySchema;

export type FindTilesRepo = z.infer<typeof findTilesRepoSchema>;

export const findCapitalProjectTilesByCityCouncilDistrictIdRepoSchema =
mvtEntitySchema;

export type FindCapitalProjectTilesByCityCouncilDistrictIdRepo = z.infer<
typeof findCapitalProjectTilesByCityCouncilDistrictIdRepoSchema
>;

export const checkByIdRepoSchema = cityCouncilDistrictEntitySchema.pick({
id: true,
});
Expand Down
59 changes: 59 additions & 0 deletions src/city-council-district/city-council-district.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
FindTilesRepo,
FindCapitalProjectsByCityCouncilDistrictIdRepo,
FindGeoJsonByIdRepo,
FindCapitalProjectTilesByCityCouncilDistrictIdRepo,
} from "./city-council-district.repository.schema";
import {
FindCapitalProjectTilesByCityCouncilDistrictIdPathParams,
FindCityCouncilDistrictGeoJsonByCityCouncilDistrictIdPathParams,
FindCityCouncilDistrictTilesPathParams,
} from "src/gen";
Expand Down Expand Up @@ -78,6 +80,63 @@ export class CityCouncilDistrictRepository {
}
}

async findCapitalProjectTilesByCityCouncilDistrictId({
cityCouncilDistrictId,
z,
x,
y,
}: FindCapitalProjectTilesByCityCouncilDistrictIdPathParams): Promise<FindCapitalProjectTilesByCityCouncilDistrictIdRepo> {
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(
cityCouncilDistrict,
sql`
ST_Intersects(${cityCouncilDistrict.mercatorFill}, ${capitalProject.mercatorFillMPoly})
OR ST_Intersects(${cityCouncilDistrict.mercatorFill}, ${capitalProject.mercatorFillMPnt})`,
)
.where(eq(cityCouncilDistrict.id, cityCouncilDistrictId))
.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();
}
}

async findTiles(
params: FindCityCouncilDistrictTilesPathParams,
): Promise<FindTilesRepo> {
Expand Down
20 changes: 20 additions & 0 deletions src/city-council-district/city-council-district.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
findCapitalProjectsByCityCouncilIdQueryResponseSchema,
findCityCouncilDistrictsQueryResponseSchema,
findCityCouncilDistrictGeoJsonByCityCouncilDistrictIdQueryResponseSchema,
findCapitalProjectTilesByCityCouncilDistrictIdQueryResponseSchema,
} from "src/gen";
import { CityCouncilDistrictService } from "./city-council-district.service";
import { ResourceNotFoundException } from "src/exception";
Expand Down Expand Up @@ -76,6 +77,25 @@ describe("City Council District service unit", () => {
});
});

describe("findCapitalProjectTilesByCityCouncilDistrictId", () => {
it("should return an mvt when requesting coordinates", async () => {
const mvt =
await cityCouncilDistrictService.findCapitalProjectTilesByCityCouncilDistrictId(
{
cityCouncilDistrictId: "1",
z: 1,
x: 1,
y: 1,
},
);
expect(() =>
findCapitalProjectTilesByCityCouncilDistrictIdQueryResponseSchema.parse(
mvt,
),
).not.toThrow();
});
});

describe("findCapitalProjectsByCityCouncilId", () => {
it("service should return a list of capital projects by city council district id, using the default limit and offset", async () => {
const { id } =
Expand Down
9 changes: 9 additions & 0 deletions src/city-council-district/city-council-district.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FindCapitalProjectsByCityCouncilIdPathParams,
FindCapitalProjectsByCityCouncilIdQueryParams,
FindCapitalProjectsByCityCouncilIdQueryResponse,
FindCapitalProjectTilesByCityCouncilDistrictIdPathParams,
FindCityCouncilDistrictGeoJsonByCityCouncilDistrictIdPathParams,
FindCityCouncilDistrictTilesPathParams,
} from "src/gen";
Expand Down Expand Up @@ -61,6 +62,14 @@ export class CityCouncilDistrictService {
};
}

async findCapitalProjectTilesByCityCouncilDistrictId(
params: FindCapitalProjectTilesByCityCouncilDistrictIdPathParams,
) {
return this.cityCouncilDistrictRepository.findCapitalProjectTilesByCityCouncilDistrictId(
params,
);
}

async findCapitalProjectsById({
limit = 20,
offset = 0,
Expand Down
48 changes: 48 additions & 0 deletions src/gen/types/FindCapitalProjectTilesByCityCouncilDistrictId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { Error } from "./Error";

export type FindCapitalProjectTilesByCityCouncilDistrictIdPathParams = {
/**
* @description One or two character code to represent city council districts.
* @type string
*/
cityCouncilDistrictId: 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 FindCapitalProjectTilesByCityCouncilDistrictId200 = string;
/**
* @description Invalid client request
*/
export type FindCapitalProjectTilesByCityCouncilDistrictId400 = Error;
/**
* @description Server side error
*/
export type FindCapitalProjectTilesByCityCouncilDistrictId500 = Error;
/**
* @description A protobuf file formatted as Mapbox Vector Tile
*/
export type FindCapitalProjectTilesByCityCouncilDistrictIdQueryResponse =
string;
export type FindCapitalProjectTilesByCityCouncilDistrictIdQuery = {
Response: FindCapitalProjectTilesByCityCouncilDistrictIdQueryResponse;
PathParams: FindCapitalProjectTilesByCityCouncilDistrictIdPathParams;
Errors:
| FindCapitalProjectTilesByCityCouncilDistrictId400
| FindCapitalProjectTilesByCityCouncilDistrictId500;
};
1 change: 1 addition & 0 deletions src/gen/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from "./FindCapitalProjectByManagingCodeCapitalProjectId";
export * from "./FindCapitalProjectGeoJsonByManagingCodeCapitalProjectId";
export * from "./FindCapitalProjectTiles";
export * from "./FindCapitalProjectTilesByBoroughIdCommunityDistrictId";
export * from "./FindCapitalProjectTilesByCityCouncilDistrictId";
export * from "./FindCapitalProjectsByBoroughIdCommunityDistrictId";
export * from "./FindCapitalProjectsByCityCouncilId";
export * from "./FindCityCouncilDistrictGeoJsonByCityCouncilDistrictId";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { z } from "zod";
import { errorSchema } from "./errorSchema";

export const findCapitalProjectTilesByCityCouncilDistrictIdPathParamsSchema =
z.object({
cityCouncilDistrictId: z.coerce
.string()
.regex(new RegExp("^([0-9]{1,2})$"))
.describe(
"One or two character code to represent city council districts.",
),
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 findCapitalProjectTilesByCityCouncilDistrictId200Schema =
z.coerce.string();
/**
* @description Invalid client request
*/
export const findCapitalProjectTilesByCityCouncilDistrictId400Schema = z.lazy(
() => errorSchema,
);
/**
* @description Server side error
*/
export const findCapitalProjectTilesByCityCouncilDistrictId500Schema = z.lazy(
() => errorSchema,
);
/**
* @description A protobuf file formatted as Mapbox Vector Tile
*/
export const findCapitalProjectTilesByCityCouncilDistrictIdQueryResponseSchema =
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 @@ -20,6 +20,7 @@ export * from "./findCapitalCommitmentsByManagingCodeCapitalProjectIdSchema";
export * from "./findCapitalProjectByManagingCodeCapitalProjectIdSchema";
export * from "./findCapitalProjectGeoJsonByManagingCodeCapitalProjectIdSchema";
export * from "./findCapitalProjectTilesByBoroughIdCommunityDistrictIdSchema";
export * from "./findCapitalProjectTilesByCityCouncilDistrictIdSchema";
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 @@ -80,6 +80,12 @@ import {
findCityCouncilDistrictGeoJsonByCityCouncilDistrictId500Schema,
findCityCouncilDistrictGeoJsonByCityCouncilDistrictIdPathParamsSchema,
} from "./findCityCouncilDistrictGeoJsonByCityCouncilDistrictIdSchema";
import {
findCapitalProjectTilesByCityCouncilDistrictIdQueryResponseSchema,
findCapitalProjectTilesByCityCouncilDistrictId400Schema,
findCapitalProjectTilesByCityCouncilDistrictId500Schema,
findCapitalProjectTilesByCityCouncilDistrictIdPathParamsSchema,
} from "./findCapitalProjectTilesByCityCouncilDistrictIdSchema";
import {
findCapitalProjectsByCityCouncilIdQueryResponseSchema,
findCapitalProjectsByCityCouncilId400Schema,
Expand Down Expand Up @@ -427,6 +433,25 @@ export const operations = {
500: findCityCouncilDistrictGeoJsonByCityCouncilDistrictId500Schema,
},
},
findCapitalProjectTilesByCityCouncilDistrictId: {
request: undefined,
parameters: {
path: findCapitalProjectTilesByCityCouncilDistrictIdPathParamsSchema,
query: undefined,
header: undefined,
},
responses: {
200: findCapitalProjectTilesByCityCouncilDistrictIdQueryResponseSchema,
400: findCapitalProjectTilesByCityCouncilDistrictId400Schema,
500: findCapitalProjectTilesByCityCouncilDistrictId500Schema,
default:
findCapitalProjectTilesByCityCouncilDistrictIdQueryResponseSchema,
},
errors: {
400: findCapitalProjectTilesByCityCouncilDistrictId400Schema,
500: findCapitalProjectTilesByCityCouncilDistrictId500Schema,
},
},
findCapitalProjectsByCityCouncilId: {
request: undefined,
parameters: {
Expand Down Expand Up @@ -741,6 +766,10 @@ export const paths = {
"/city-council-districts/{cityCouncilDistrictId}/geojson": {
get: operations["findCityCouncilDistrictGeoJsonByCityCouncilDistrictId"],
},
"/city-council-districts/{cityCouncilDistrictId}/capital-projects/{z}/{x}/{y}.pbf":
{
get: operations["findCapitalProjectTilesByCityCouncilDistrictId"],
},
"/city-council-districts/{cityCouncilDistrictId}/capital-projects": {
get: operations["findCapitalProjectsByCityCouncilId"],
},
Expand Down
Loading