From aba4f284e924e3ceb2e34b15ca2a99605e094e8a Mon Sep 17 00:00:00 2001 From: Ossi Tammi <> Date: Mon, 11 Mar 2024 14:23:09 +0200 Subject: [PATCH 1/3] Store geometries in given coordinate system --- server/src/application/survey.ts | 20 +++++++++++++++----- server/src/database.ts | 16 +++++++--------- server/src/utils.ts | 9 +++++---- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/server/src/application/survey.ts b/server/src/application/survey.ts index cb24470a..06beb78d 100644 --- a/server/src/application/survey.ts +++ b/server/src/application/survey.ts @@ -196,12 +196,13 @@ type DBSurveyJoin = DBSurvey & { theme_name: string; theme_data: SurveyTheme; default_map_view: Geometry; + mapViewSRID: number; }; /** * Helper function for creating survey page column set for database queries */ -const surveyPageColumnSet = getColumnSet('survey_page', [ +const surveyPageColumnSet = (inputSRID: number) => getColumnSet('survey_page', [ 'id', 'survey_id', 'idx', @@ -224,7 +225,7 @@ const surveyPageColumnSet = getColumnSet('survey_page', [ cast: 'json', }, 'sidebar_image_size', - getGeoJSONColumn('default_map_view', 3067), + getGeoJSONColumn('default_map_view', inputSRID), ]); /** @@ -289,7 +290,8 @@ export async function getSurvey(params: { id: number } | { name: string }) { page.sidebar_image_name as page_sidebar_image_name, page.sidebar_image_alt_text as page_sidebar_image_alt_text, page.sidebar_image_size as page_sidebar_image_size, - public.ST_AsGeoJSON(public.ST_Transform(page.default_map_view, 3067))::json as default_map_view + public.ST_AsGeoJSON(page.default_map_view)::json as default_map_view, + public.ST_SRID(page.default_map_view) AS "mapViewSRID" FROM ( SELECT @@ -954,11 +956,18 @@ export async function updateSurvey(survey: Survey) { throw new NotFoundError(`Survey with ID ${survey.id} not found`); } + console.log(survey.pages[0].sidebar.defaultMapView) + // Find out what coordinate system was used for the default map view + const pageWithDefaultMapView = survey.pages.find(page => page.sidebar.defaultMapView); + const defaultMapViewSRID = pageWithDefaultMapView ? parseInt(pageWithDefaultMapView.sidebar.defaultMapView.crs.split(':')[1]) : null; + + console.log(defaultMapViewSRID) + // Update the survey pages await getDb().none( getMultiUpdateQuery( surveyPagesToRows(survey.pages, survey.id), - surveyPageColumnSet, + surveyPageColumnSet(defaultMapViewSRID), ) + ' WHERE t.id = v.id', ); @@ -1270,8 +1279,9 @@ function dbSurveyJoinToPage(dbSurveyJoin: DBSurveyJoin): SurveyPage { ? geometryToGeoJSONFeatureCollection( dbSurveyJoin.default_map_view, {}, + dbSurveyJoin.mapViewSRID ) - : null, + : null }, conditions: {}, }; diff --git a/server/src/database.ts b/server/src/database.ts index 800c0de3..58fb4774 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -1,15 +1,13 @@ +import retry from 'async-retry'; +import migrate from 'node-pg-migrate'; +import { Client } from 'pg'; import PgPromise from 'pg-promise'; import { IClient } from 'pg-promise/typescript/pg-subset'; -import { Client } from 'pg'; -import migrate from 'node-pg-migrate'; import logger from './logger'; -import retry from 'async-retry'; // Default schemas for all queries const schema = ['application', 'public', 'data']; -// Default SRID for geometries - inserted data will be transformed to this -const defaultSrid = 3857; // How many times to retry connection, if it fails on initialization? const connectRetries = Number(process.env.DATABASE_CONNECT_RETRIES) || 10; @@ -112,13 +110,13 @@ export function getGeoJSONColumn( : value.properties?.bufferRadius != null ? // If geometry provided with buffer radius, add ST_Buffer pgp.as.format( - 'public.ST_Buffer(public.ST_Transform(public.ST_SetSRID(public.ST_GeomFromGeoJSON($1), $2), $3), $4)', - [value, inputSRID, defaultSrid, value.properties.bufferRadius], + 'public.ST_Buffer(public.ST_SetSRID(public.ST_GeomFromGeoJSON($1), $2), $4)', + [value, inputSRID, value.properties.bufferRadius], ) : pgp.as.format( // Transform provided geometry to default SRID - 'public.ST_Transform(public.ST_SetSRID(public.ST_GeomFromGeoJSON($1), $2), $3)', - [value, inputSRID, defaultSrid], + 'public.ST_SetSRID(public.ST_GeomFromGeoJSON($1), $2)', + [value, inputSRID], ); }, }; diff --git a/server/src/utils.ts b/server/src/utils.ts index bd1c1277..c63ce834 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -1,9 +1,9 @@ +import { MimeType } from '@interfaces/admin'; +import { ImageType } from '@interfaces/survey'; import { NextFunction, Request, Response } from 'express'; import { ValidationChain, validationResult } from 'express-validator'; -import { BadRequestError } from './error'; -import { ImageType } from '@interfaces/survey'; -import { MimeType } from '@interfaces/admin'; import { Geometry } from 'geojson'; +import { BadRequestError } from './error'; /** * Middleware function for validating a request against provided validation rules. @@ -90,10 +90,11 @@ export function indexToAlpha(num = 1) { export function geometryToGeoJSONFeatureCollection( geometry: Geometry, properties: Record, + srid: number ): GeoJSON.FeatureCollection & { crs: string } { return { type: 'FeatureCollection', - crs: 'EPSG:3067', + crs: `EPSG:${srid}`, features: [{ type: 'Feature', geometry: geometry, properties }], }; } From 0dab369d671497ec6db704167b1ac7a1aef5b314 Mon Sep 17 00:00:00 2001 From: Ossi Tammi <> Date: Mon, 11 Mar 2024 18:00:01 +0200 Subject: [PATCH 2/3] Added srid information to geopackage export --- server/src/application/answer.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/server/src/application/answer.ts b/server/src/application/answer.ts index d5c35c81..b643d30a 100644 --- a/server/src/application/answer.ts +++ b/server/src/application/answer.ts @@ -33,6 +33,7 @@ interface DBAnswerEntry { language: LanguageCode; title: LocalizedText; type: string; + geometry_srid?: number; value_geometry: GeoJSON.Point | GeoJSON.LineString | GeoJSON.Polygon; value_text: string; value_json: JSON[]; @@ -75,6 +76,7 @@ interface AnswerEntry { submissionLanguage: LanguageCode; title: LocalizedText; type: string; + geometrySRID: number; valueGeometry: GeoJSON.Point | GeoJSON.LineString | GeoJSON.Polygon; valueText: string; valueJson: JSON[]; @@ -176,6 +178,7 @@ function dbAnswerEntryRowsToAnswerEntries(rows: DBAnswerEntry[]) { submissionLanguage: row?.language, title: row.title, type: row.type, + geometrySRID: row.geometry_srid, valueGeometry: row.value_geometry, valueText: row.value_text, valueJson: row.value_json, @@ -379,12 +382,15 @@ export async function getCSVFile(surveyId: number): Promise { */ export async function getGeoPackageFile(surveyId: number): Promise { const rows = await getGeometryDBEntries(surveyId); + + const srid = rows?.find(row => row.geometrySRID)?.geometrySRID ?? '3857'; + console.log(srid); const checkboxOptions = await getCheckboxOptionsFromDB(surveyId); const mapLayers = await getSurvey({ id: surveyId }).then((survey) => getAvailableMapLayers(survey.mapUrl), ); - if (!rows || !checkboxOptions) { + if (!rows) { return null; } @@ -419,7 +425,7 @@ export async function getGeoPackageFile(surveyId: number): Promise { features: firstFeatures, crs: { type: 'name', - properties: { name: 'urn:ogc:def:crs:EPSG::3067' }, + properties: { name: `urn:ogc:def:crs:EPSG::${srid}` }, }, }, { @@ -436,7 +442,7 @@ export async function getGeoPackageFile(surveyId: number): Promise { features, crs: { type: 'name', - properties: { name: 'urn:ogc:def:crs:EPSG::3067' }, + properties: { name: `urn:ogc:def:crs:EPSG::${srid}` }, }, }, { @@ -578,6 +584,7 @@ async function getGeometryDBEntries(surveyId: number): Promise { ae.value_option_id, opt.text as option_text, public.ST_AsGeoJSON(ae.value_geometry)::json as value_geometry, + public.ST_SRID(ae.value_geometry) AS geometry_srid, ae.value_numeric, ae.value_json, ae.parent_entry_id, From 616725748d168a2d4ad8a823f75fa78e53a1c279 Mon Sep 17 00:00:00 2001 From: Ossi Tammi <> Date: Mon, 11 Mar 2024 18:04:19 +0200 Subject: [PATCH 3/3] Removed console logs --- server/src/application/answer.ts | 1 - server/src/application/survey.ts | 3 --- 2 files changed, 4 deletions(-) diff --git a/server/src/application/answer.ts b/server/src/application/answer.ts index b643d30a..4e72e932 100644 --- a/server/src/application/answer.ts +++ b/server/src/application/answer.ts @@ -384,7 +384,6 @@ export async function getGeoPackageFile(surveyId: number): Promise { const rows = await getGeometryDBEntries(surveyId); const srid = rows?.find(row => row.geometrySRID)?.geometrySRID ?? '3857'; - console.log(srid); const checkboxOptions = await getCheckboxOptionsFromDB(surveyId); const mapLayers = await getSurvey({ id: surveyId }).then((survey) => getAvailableMapLayers(survey.mapUrl), diff --git a/server/src/application/survey.ts b/server/src/application/survey.ts index 06beb78d..72b91ba3 100644 --- a/server/src/application/survey.ts +++ b/server/src/application/survey.ts @@ -956,13 +956,10 @@ export async function updateSurvey(survey: Survey) { throw new NotFoundError(`Survey with ID ${survey.id} not found`); } - console.log(survey.pages[0].sidebar.defaultMapView) // Find out what coordinate system was used for the default map view const pageWithDefaultMapView = survey.pages.find(page => page.sidebar.defaultMapView); const defaultMapViewSRID = pageWithDefaultMapView ? parseInt(pageWithDefaultMapView.sidebar.defaultMapView.crs.split(':')[1]) : null; - console.log(defaultMapViewSRID) - // Update the survey pages await getDb().none( getMultiUpdateQuery(