map layer management
This commit is contained in:
13
api/config.ts
Normal file
13
api/config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const FALLBACK_API_BASE_URL = "http://localhost:3000";
|
||||
|
||||
export const API_BASE_URL =
|
||||
process.env.NEXT_PUBLIC_API_BASE_URL || FALLBACK_API_BASE_URL;
|
||||
|
||||
export const API_ENDPOINTS = {
|
||||
geometries: `${API_BASE_URL}/geometries`,
|
||||
geometriesBatch: `${API_BASE_URL}/geometries/batch`,
|
||||
vectorTiles: `${API_BASE_URL}/tiles/{z}/{x}/{y}`,
|
||||
rasterTiles: `${API_BASE_URL}/raster-tiles/{z}/{x}/{y}`,
|
||||
vectorTilesMetadata: `${API_BASE_URL}/tiles/metadata/info`,
|
||||
rasterTilesMetadata: `${API_BASE_URL}/raster-tiles/metadata/info`,
|
||||
} as const;
|
||||
83
api/geometries.ts
Normal file
83
api/geometries.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { API_ENDPOINTS } from "@/api/config";
|
||||
import { requestJson } from "@/api/http";
|
||||
import { Change, FeatureCollection, Geometry } from "@/lib/useEditorState";
|
||||
|
||||
export type GeometriesBBoxQuery = {
|
||||
minLng: number;
|
||||
minLat: number;
|
||||
maxLng: number;
|
||||
maxLat: number;
|
||||
time?: number;
|
||||
};
|
||||
|
||||
export type GeometryCreatePayload = {
|
||||
geometry: Geometry;
|
||||
time_start?: number | null;
|
||||
time_end?: number | null;
|
||||
kind?: string | null;
|
||||
};
|
||||
|
||||
export type GeometryUpdatePayload = {
|
||||
geometry: Geometry;
|
||||
time_start?: number | null;
|
||||
time_end?: number | null;
|
||||
};
|
||||
|
||||
export type GeometryCreateResponse = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type BatchSaveResponse = {
|
||||
success: boolean;
|
||||
applied: number;
|
||||
};
|
||||
|
||||
function buildBBoxQueryString(params: GeometriesBBoxQuery): string {
|
||||
const query = new URLSearchParams({
|
||||
minLng: String(params.minLng),
|
||||
minLat: String(params.minLat),
|
||||
maxLng: String(params.maxLng),
|
||||
maxLat: String(params.maxLat),
|
||||
});
|
||||
|
||||
if (params.time !== undefined) {
|
||||
query.set("time", String(params.time));
|
||||
}
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
export async function fetchGeometriesByBBox(params: GeometriesBBoxQuery): Promise<FeatureCollection> {
|
||||
const url = `${API_ENDPOINTS.geometries}?${buildBBoxQueryString(params)}`;
|
||||
return requestJson<FeatureCollection>(url);
|
||||
}
|
||||
|
||||
export async function saveGeometryBatchChanges(changes: Change[]): Promise<BatchSaveResponse> {
|
||||
return requestJson<BatchSaveResponse>(API_ENDPOINTS.geometriesBatch, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ changes }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function createGeometry(payload: GeometryCreatePayload): Promise<GeometryCreateResponse> {
|
||||
return requestJson<GeometryCreateResponse>(API_ENDPOINTS.geometries, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateGeometry(id: string | number, payload: GeometryUpdatePayload): Promise<{ success: boolean }> {
|
||||
return requestJson<{ success: boolean }>(`${API_ENDPOINTS.geometries}/${id}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteGeometry(id: string | number): Promise<{ success: boolean }> {
|
||||
return requestJson<{ success: boolean }>(`${API_ENDPOINTS.geometries}/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
22
api/http.ts
Normal file
22
api/http.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
body: string;
|
||||
|
||||
constructor(message: string, status: number, body: string) {
|
||||
super(message);
|
||||
this.name = "ApiError";
|
||||
this.status = status;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
|
||||
export async function requestJson<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(input, init);
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new ApiError(`Request failed with status ${res.status}`, res.status, text);
|
||||
}
|
||||
|
||||
return (await res.json()) as T;
|
||||
}
|
||||
20
api/tiles.ts
Normal file
20
api/tiles.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { API_ENDPOINTS } from "@/api/config";
|
||||
import { requestJson } from "@/api/http";
|
||||
|
||||
export type TileMetadata = Record<string, string>;
|
||||
|
||||
export function getVectorTileTemplateUrl(): string {
|
||||
return API_ENDPOINTS.vectorTiles;
|
||||
}
|
||||
|
||||
export function getRasterTileTemplateUrl(): string {
|
||||
return API_ENDPOINTS.rasterTiles;
|
||||
}
|
||||
|
||||
export async function fetchVectorTilesMetadata(): Promise<TileMetadata> {
|
||||
return requestJson<TileMetadata>(API_ENDPOINTS.vectorTilesMetadata);
|
||||
}
|
||||
|
||||
export async function fetchRasterTilesMetadata(): Promise<TileMetadata> {
|
||||
return requestJson<TileMetadata>(API_ENDPOINTS.rasterTilesMetadata);
|
||||
}
|
||||
Reference in New Issue
Block a user