From e6b3dc05d326a6ec7d4e34a85e9b20352fc8a938 Mon Sep 17 00:00:00 2001 From: cracklesparkle Date: Tue, 14 Jan 2025 14:32:41 +0900 Subject: [PATCH] Region/district/city bounds routes --- client/package-lock.json | 46 +++++++++------------------ client/package.json | 1 + client/yarn.lock | 26 ++++++++-------- ems/package.json | 3 +- ems/src/api/gis/index.ts | 65 +++++++++++++++++++++++++++++++++++++++ ems/src/utils/postgres.ts | 37 ++++++++++++++++++++++ 6 files changed, 132 insertions(+), 46 deletions(-) create mode 100644 ems/src/utils/postgres.ts diff --git a/client/package-lock.json b/client/package-lock.json index f08e53d..9047477 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -43,6 +43,7 @@ "dayjs": "^1.11.13", "embla-carousel-react": "^8.3.0", "file-type": "^19.0.0", + "jspdf": "^2.5.2", "ol": "^10.0.0", "ol-ext": "^4.0.23", "proj4": "^2.12.0", @@ -4002,8 +4003,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "optional": true, - "peer": true + "optional": true }, "node_modules/@types/react": { "version": "18.3.3", @@ -4516,7 +4516,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "peer": true, "bin": { "atob": "bin/atob.js" }, @@ -4644,7 +4643,6 @@ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "optional": true, - "peer": true, "engines": { "node": ">= 0.6.0" } @@ -4922,7 +4920,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "peer": true, "bin": { "btoa": "bin/btoa.js" }, @@ -5071,7 +5068,6 @@ "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", "optional": true, - "peer": true, "dependencies": { "@babel/runtime": "^7.12.5", "@types/raf": "^3.4.0", @@ -5090,8 +5086,7 @@ "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "optional": true, - "peer": true + "optional": true }, "node_modules/chalk": { "version": "4.1.2", @@ -5430,7 +5425,6 @@ "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", "hasInstallScript": true, "optional": true, - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -5567,7 +5561,6 @@ "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "optional": true, - "peer": true, "dependencies": { "utrie": "^1.0.2" } @@ -5945,8 +5938,7 @@ "version": "2.5.6", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz", "integrity": "sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==", - "optional": true, - "peer": true + "optional": true }, "node_modules/earcut": { "version": "3.0.0", @@ -6545,10 +6537,9 @@ } }, "node_modules/fflate": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", - "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", - "peer": true + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -7148,7 +7139,6 @@ "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "optional": true, - "peer": true, "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" @@ -7874,20 +7864,19 @@ } }, "node_modules/jspdf": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz", - "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==", - "peer": true, + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz", + "integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==", "dependencies": { - "@babel/runtime": "^7.14.0", + "@babel/runtime": "^7.23.2", "atob": "^2.1.2", "btoa": "^1.2.1", - "fflate": "^0.4.8" + "fflate": "^0.8.1" }, "optionalDependencies": { "canvg": "^3.0.6", "core-js": "^3.6.0", - "dompurify": "^2.2.0", + "dompurify": "^2.5.4", "html2canvas": "^1.0.0-rc.5" } }, @@ -8704,8 +8693,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "optional": true, - "peer": true + "optional": true }, "node_modules/picocolors": { "version": "1.1.0", @@ -9309,7 +9297,6 @@ "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", "optional": true, - "peer": true, "dependencies": { "performance-now": "^2.1.0" } @@ -9878,7 +9865,6 @@ "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", "optional": true, - "peer": true, "engines": { "node": ">= 0.8.15" } @@ -10751,7 +10737,6 @@ "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", "optional": true, - "peer": true, "engines": { "node": ">=0.1.14" } @@ -11100,7 +11085,6 @@ "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", "optional": true, - "peer": true, "engines": { "node": ">=12.0.0" } @@ -11227,7 +11211,6 @@ "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "optional": true, - "peer": true, "dependencies": { "utrie": "^1.0.2" } @@ -11749,7 +11732,6 @@ "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "optional": true, - "peer": true, "dependencies": { "base64-arraybuffer": "^1.0.2" } diff --git a/client/package.json b/client/package.json index fab82be..5244eb7 100644 --- a/client/package.json +++ b/client/package.json @@ -46,6 +46,7 @@ "dayjs": "^1.11.13", "embla-carousel-react": "^8.3.0", "file-type": "^19.0.0", + "jspdf": "^2.5.2", "ol": "^10.0.0", "ol-ext": "^4.0.23", "proj4": "^2.12.0", diff --git a/client/yarn.lock b/client/yarn.lock index 5902565..7ddb93b 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -936,7 +936,7 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.25.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.20.13", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.26.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -2909,7 +2909,7 @@ domain-browser@^4.22.0: resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz" integrity sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA== -dompurify@^2.2.0: +dompurify@^2.5.4: version "2.5.6" resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.5.6.tgz" integrity sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ== @@ -3303,10 +3303,10 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fflate@^0.4.8: - version "0.4.8" - resolved "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz" - integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== +fflate@^0.8.1: + version "0.8.2" + resolved "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== file-entry-cache@^6.0.1: version "6.0.1" @@ -4062,19 +4062,19 @@ jsonpointer@^5.0.0: resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== -jspdf@^2.5.1: - version "2.5.1" - resolved "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz" - integrity sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA== +jspdf@^2.5.1, jspdf@^2.5.2: + version "2.5.2" + resolved "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz" + integrity sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ== dependencies: - "@babel/runtime" "^7.14.0" + "@babel/runtime" "^7.23.2" atob "^2.1.2" btoa "^1.2.1" - fflate "^0.4.8" + fflate "^0.8.1" optionalDependencies: canvg "^3.0.6" core-js "^3.6.0" - dompurify "^2.2.0" + dompurify "^2.5.4" html2canvas "^1.0.0-rc.5" keyv@^4.5.3: diff --git a/ems/package.json b/ems/package.json index 299e7f8..ca85d7a 100644 --- a/ems/package.json +++ b/ems/package.json @@ -23,7 +23,7 @@ "ioredis": "^5.4.1", "md5": "^2.3.0", "multer": "^1.4.5-lts.1", - "pg": "^8.13.0", + "pg": "^8.13.1", "pump": "^3.0.0", "sharp": "^0.33.5", "tedious": "^18.6.1" @@ -35,6 +35,7 @@ "@types/md5": "^2.3.5", "@types/multer": "^1.4.12", "@types/node": "^22.4.1", + "@types/pg": "^8.11.10", "@types/pump": "^1.1.3", "@types/redis": "^4.0.11", "nodemon": "^3.1.4", diff --git a/ems/src/api/gis/index.ts b/ems/src/api/gis/index.ts index 8ac428f..077d254 100644 --- a/ems/src/api/gis/index.ts +++ b/ems/src/api/gis/index.ts @@ -1,8 +1,73 @@ import express, { Request, Response } from 'express'; import { tediousQuery } from '../../utils/tedious'; import { GeneralDB, GisDB } from '../../constants/db'; +import { pgQuery } from '../../utils/postgres'; const router = express.Router() +router.get('/bounds/:entity_type', async (req: Request, res: Response) => { + try { + const { entity_type } = req.params + + const result = await pgQuery( + ` + SELECT * FROM bounds + WHERE entity_type = $1 + `, + [entity_type] + ) + + if (Array.isArray(result)) { + if (result.length > 0) { + const geometries = result.map((bound: { id: number, entity_id: number, entity_type: string, geometry: JSON, published_at: string, deleted_at: string | null }) => { + return { + ...bound.geometry, + properties: { + id: bound.id, + entity_id: bound.entity_id, + entity_type: bound.entity_type + } + } + }) + + res.status(200).json(geometries) + } else { + res.status(404).json('not found') + } + } else { + res.status(404).json('not found') + } + } catch (err) { + res.status(500) + } +}) + +router.get('/bounds/:entity_type/:entity_id', async (req: Request, res: Response) => { + try { + const { entity_type, entity_id } = req.params + + const result = await pgQuery( + ` + SELECT * FROM bounds + WHERE entity_type = $1 + AND entity_id = $2 + `, + [entity_type, entity_id] + ) + + if (Array.isArray(result)) { + if (result.length > 0) { + res.status(200).json(result[0].geometry) + } else { + res.status(404).json('not found') + } + } else { + res.status(404).json('not found') + } + } catch (err) { + res.status(500) + } +}) + router.get('/images/all', async (req: Request, res: Response) => { try { const { offset, limit, city_id } = req.query diff --git a/ems/src/utils/postgres.ts b/ems/src/utils/postgres.ts new file mode 100644 index 0000000..e54b9c9 --- /dev/null +++ b/ems/src/utils/postgres.ts @@ -0,0 +1,37 @@ +import { Pool } from 'pg'; +import 'dotenv/config'; + +// Environment variables for database configuration +const PG_HOST = process.env.PG_HOST || 'localhost'; +const PG_PORT = Number(process.env.PG_PORT) || 5432; +const PG_USER = process.env.PG_USER || 'postgres'; +const PG_PASSWORD = process.env.PG_PASSWORD || ''; +const PG_DB = process.env.PG_DB || 'postgres'; + +// Create a connection pool +const pool = new Pool({ + host: PG_HOST, + port: PG_PORT, + user: PG_USER, + password: PG_PASSWORD, + database: PG_DB, +}); + +export async function pgQuery(query: string, params: any[] = []) { + try { + // Get a client from the pool + const client = await pool.connect(); + try { + // Execute the query with parameters + const result = await client.query(query, params); + return result.rows; // Return only the rows + } finally { + // Release the client back to the pool + client.release(); + } + } catch (err) { + // Log error and rethrow it + console.error(`Error executing query: ${query}`, err); + throw err; + } +} \ No newline at end of file