Skip to content

Commit

Permalink
Merge pull request #5 from Oulunkaupunki/feature/multi-projection
Browse files Browse the repository at this point in the history
Feature/multi projection
  • Loading branch information
ossitammi authored Mar 11, 2024
2 parents 1b2f94e + 6167257 commit f483d94
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 21 deletions.
12 changes: 9 additions & 3 deletions server/src/application/answer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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[];
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -379,12 +382,14 @@ export async function getCSVFile(surveyId: number): Promise<string> {
*/
export async function getGeoPackageFile(surveyId: number): Promise<Buffer> {
const rows = await getGeometryDBEntries(surveyId);

const srid = rows?.find(row => row.geometrySRID)?.geometrySRID ?? '3857';
const checkboxOptions = await getCheckboxOptionsFromDB(surveyId);
const mapLayers = await getSurvey({ id: surveyId }).then((survey) =>
getAvailableMapLayers(survey.mapUrl),
);

if (!rows || !checkboxOptions) {
if (!rows) {
return null;
}

Expand Down Expand Up @@ -419,7 +424,7 @@ export async function getGeoPackageFile(surveyId: number): Promise<Buffer> {
features: firstFeatures,
crs: {
type: 'name',
properties: { name: 'urn:ogc:def:crs:EPSG::3067' },
properties: { name: `urn:ogc:def:crs:EPSG::${srid}` },
},
},
{
Expand All @@ -436,7 +441,7 @@ export async function getGeoPackageFile(surveyId: number): Promise<Buffer> {
features,
crs: {
type: 'name',
properties: { name: 'urn:ogc:def:crs:EPSG::3067' },
properties: { name: `urn:ogc:def:crs:EPSG::${srid}` },
},
},
{
Expand Down Expand Up @@ -578,6 +583,7 @@ async function getGeometryDBEntries(surveyId: number): Promise<AnswerEntry[]> {
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,
Expand Down
17 changes: 12 additions & 5 deletions server/src/application/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<DBSurveyPage>('survey_page', [
const surveyPageColumnSet = (inputSRID: number) => getColumnSet<DBSurveyPage>('survey_page', [
'id',
'survey_id',
'idx',
Expand All @@ -224,7 +225,7 @@ const surveyPageColumnSet = getColumnSet<DBSurveyPage>('survey_page', [
cast: 'json',
},
'sidebar_image_size',
getGeoJSONColumn('default_map_view', 3067),
getGeoJSONColumn('default_map_view', inputSRID),
]);

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -954,11 +956,15 @@ export async function updateSurvey(survey: Survey) {
throw new NotFoundError(`Survey with ID ${survey.id} not found`);
}

// 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;

// Update the survey pages
await getDb().none(
getMultiUpdateQuery(
surveyPagesToRows(survey.pages, survey.id),
surveyPageColumnSet,
surveyPageColumnSet(defaultMapViewSRID),
) + ' WHERE t.id = v.id',
);

Expand Down Expand Up @@ -1270,8 +1276,9 @@ function dbSurveyJoinToPage(dbSurveyJoin: DBSurveyJoin): SurveyPage {
? geometryToGeoJSONFeatureCollection(
dbSurveyJoin.default_map_view,
{},
dbSurveyJoin.mapViewSRID
)
: null,
: null
},
conditions: {},
};
Expand Down
16 changes: 7 additions & 9 deletions server/src/database.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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],
);
},
};
Expand Down
9 changes: 5 additions & 4 deletions server/src/utils.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -90,10 +90,11 @@ export function indexToAlpha(num = 1) {
export function geometryToGeoJSONFeatureCollection(
geometry: Geometry,
properties: Record<string, string>,
srid: number
): GeoJSON.FeatureCollection & { crs: string } {
return {
type: 'FeatureCollection',
crs: 'EPSG:3067',
crs: `EPSG:${srid}`,
features: [{ type: 'Feature', geometry: geometry, properties }],
};
}

0 comments on commit f483d94

Please sign in to comment.