1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2024-11-03 21:04:03 +08:00

Fixing GPX processing for files without paths, only markers #456 #504

This commit is contained in:
Patrik J. Braun 2023-03-11 11:39:16 +01:00
parent 83eeaf638f
commit 7653f2ce23
3 changed files with 76 additions and 64 deletions

View File

@ -1,7 +1,8 @@
import {NextFunction, Request, Response} from 'express';
import * as fs from 'fs';
import { Config } from '../../common/config/private/Config';
import { GPXProcessing } from '../model/fileprocessing/GPXProcessing';
import {Config} from '../../common/config/private/Config';
import {GPXProcessing} from '../model/fileprocessing/GPXProcessing';
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
export class MetaFileMWs {
public static async compressGPX(
@ -17,22 +18,31 @@ export class MetaFileMWs {
return res.redirect(req.originalUrl.slice(0, -1 * '\\bestFit'.length));
}
const fullPath = req.resultPipe as string;
try {
const compressedGPX = GPXProcessing.generateConvertedPath(
fullPath,
);
const compressedGPX = GPXProcessing.generateConvertedPath(
fullPath,
);
// check if converted photo exist
if (fs.existsSync(compressedGPX) === true) {
req.resultPipe = compressedGPX;
return next();
}
// check if converted photo exist
if (fs.existsSync(compressedGPX) === true) {
req.resultPipe = compressedGPX;
return next();
if (Config.MetaFile.GPXCompressing.onTheFly === true) {
req.resultPipe = await GPXProcessing.compressGPX(fullPath);
return next();
}
} catch (err) {
return next(
new ErrorDTO(
ErrorCodes.METAFILE_ERROR,
'Error during compressingGPX: ' + fullPath,
err
)
);
}
if (Config.MetaFile.GPXCompressing.onTheFly === true) {
req.resultPipe = await GPXProcessing.compressGPX(fullPath);
return next();
}
// not converted and won't be now
return res.redirect(req.originalUrl.slice(0, -1 * '\\bestFit'.length));
}

View File

@ -82,48 +82,49 @@ export class GPXProcessing {
await fsp.mkdir(outDir, {recursive: true});
const gpxStr = await fsp.readFile(filePath);
const gpxObj = await (new xml2js.Parser()).parseStringPromise(gpxStr);
const items: gpxEntry[] = gpxObj.gpx.trk[0].trkseg[0].trkpt;
const items: gpxEntry[] = gpxObj.gpx?.trk?.[0]?.trkseg[0]?.trkpt;
const distance = (entry1: gpxEntry, entry2: gpxEntry) => {
const lat1 = parseFloat(entry1.$.lat);
const lon1 = parseFloat(entry1.$.lon);
const lat2 = parseFloat(entry2.$.lat);
const lon2 = parseFloat(entry2.$.lon);
if (items) { // only compress paths
const distance = (entry1: gpxEntry, entry2: gpxEntry) => {
const lat1 = parseFloat(entry1.$.lat);
const lon1 = parseFloat(entry1.$.lon);
const lat2 = parseFloat(entry2.$.lat);
const lon2 = parseFloat(entry2.$.lon);
// credits to: https://www.movable-type.co.uk/scripts/latlong.html
const R = 6371e3; // metres
const φ1 = lat1 * Math.PI / 180; // φ, λ in radians
const φ2 = lat2 * Math.PI / 180;
const Δφ = (lat2 - lat1) * Math.PI / 180;
const Δλ = (lon2 - lon1) * Math.PI / 180;
// credits to: https://www.movable-type.co.uk/scripts/latlong.html
const R = 6371e3; // metres
const φ1 = lat1 * Math.PI / 180; // φ, λ in radians
const φ2 = lat2 * Math.PI / 180;
const Δφ = (lat2 - lat1) * Math.PI / 180;
const Δλ = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = R * c; // in metres
return d;
};
const gpxEntryFilter = (value: gpxEntry, i: number, list: gpxEntry[]) => {
if (i === 0 || i >= list.length - 1) { // always keep the first and last items
return true;
}
const timeDelta = (Date.parse(list[i].time[0]) - Date.parse(list[i - 1].time[0])); // mill sec.
const dist = distance(list[i - 1], list[i]); // meters
const d = R * c; // in metres
return d;
};
const gpxEntryFilter = (value: gpxEntry, i: number, list: gpxEntry[]) => {
if (i === 0 || i >= list.length - 1) { // always keep the first and last items
return true;
}
const timeDelta = (Date.parse(list[i].time[0]) - Date.parse(list[i - 1].time[0])); // mill sec.
const dist = distance(list[i - 1], list[i]); // meters
return !(timeDelta < Config.MetaFile.GPXCompressing.minTimeDistance &&
dist < Config.MetaFile.GPXCompressing.minDistance);
};
gpxObj.gpx.trk[0].trkseg[0].trkpt = items.filter(gpxEntryFilter).map((v) => {
v.$.lon = parseFloat(v.$.lon).toFixed(6);
v.$.lat = parseFloat(v.$.lat).toFixed(6);
delete v.ele;
delete v.extensions;
return v;
});
return !(timeDelta < Config.MetaFile.GPXCompressing.minTimeDistance &&
dist < Config.MetaFile.GPXCompressing.minDistance);
};
gpxObj.gpx.trk[0].trkseg[0].trkpt = items.filter(gpxEntryFilter).map((v) => {
v.$.lon = parseFloat(v.$.lon).toFixed(6);
v.$.lat = parseFloat(v.$.lat).toFixed(6);
delete v.ele;
delete v.extensions;
return v;
});
}
await fsp.writeFile(outPath, (new xml2js.Builder({renderOpts: {pretty: false}})).buildObject(gpxObj));
return outPath;

View File

@ -7,24 +7,25 @@ export enum ErrorCodes {
PERMISSION_DENIED = 4,
CREDENTIAL_NOT_FOUND = 5,
USER_CREATION_ERROR = 6,
USER_CREATION_ERROR = 20,
GENERAL_ERROR = 7,
THUMBNAIL_GENERATION_ERROR = 8,
PHOTO_GENERATION_ERROR = 9,
PERSON_ERROR = 10,
SERVER_ERROR = 11,
GENERAL_ERROR = 31,
THUMBNAIL_GENERATION_ERROR = 32,
PHOTO_GENERATION_ERROR = 33,
PERSON_ERROR = 34,
METAFILE_ERROR = 35,
SERVER_ERROR = 36,
USER_MANAGEMENT_DISABLED = 12,
USER_MANAGEMENT_DISABLED = 40,
INPUT_ERROR = 13,
INPUT_ERROR = 50,
SETTINGS_ERROR = 14,
TASK_ERROR = 15,
JOB_ERROR = 16,
LocationLookUp_ERROR = 17,
SETTINGS_ERROR = 60,
TASK_ERROR = 61,
JOB_ERROR = 62,
LocationLookUp_ERROR = 63,
ALBUM_ERROR = 18,
ALBUM_ERROR = 70,
}
export class ErrorDTO {