frontend/src/utils/openEditor.js (200 lines of code) (raw):
import { API_URL, ID_EDITOR_URL, POTLATCH2_EDITOR_URL } from '../config';
import { getCentroidAndZoomFromSelectedTasks, getSelectedTasksBBox } from './tasksGeometry';
export function openEditor(
editor,
project,
tasks,
selectedTasks,
windowSize,
windowObjectReference,
) {
if (editor === 'JOSM') {
sendJosmCommands(project, tasks, selectedTasks, windowSize);
return '?editor=JOSM';
}
const { center, zoom } = getCentroidAndZoomFromSelectedTasks(tasks, selectedTasks, windowSize);
if (['ID', 'RAPID'].includes(editor)) {
return getIdUrl(project, center, zoom, selectedTasks, '?editor=' + editor);
}
if (windowObjectReference == null || windowObjectReference.closed) {
windowObjectReference = window.open('', `iD-${project}-${selectedTasks}`);
}
if (editor === 'POTLATCH_2') {
windowObjectReference.location.href = getPotlatch2Url(center, zoom);
return '?editor=POTLATCH_2';
}
if (editor === 'FIELD_PAPERS') {
windowObjectReference.location.href = getFieldPapersUrl(center, zoom);
return '?editor=FIELD_PAPERS';
}
if (editor === 'CUSTOM') {
const customUrl = project.customEditor.url;
windowObjectReference.location.href = getIdUrl(project, center, zoom, selectedTasks, customUrl);
return '?editor=CUSTOM';
}
}
export function formatImageryUrl(imageryURL) {
// url is supposed to look like tms[22]:http://hiu...
if (imageryURL) {
return imageryURL.substring(imageryURL.indexOf('http')).replace('zoom', 'z');
}
}
export function getTaskGpxUrl(projectId, selectedTasks) {
return new URL(
`projects/${projectId}/tasks/queries/gpx/?tasks=${selectedTasks.join(',')}`,
API_URL,
).href;
}
export function getTaskXmlUrl(projectId, selectedTasks) {
return new URL(
`projects/${projectId}/tasks/queries/xml/?tasks=${selectedTasks.join(',')}`,
API_URL,
);
}
export function getFieldPapersUrl(centroid, zoomLevel) {
return `http://fieldpapers.org/compose#${[
zoomLevel,
roundToDecimals(centroid[1], 5),
roundToDecimals(centroid[0], 5),
].join('/')}`;
}
export function getPotlatch2Url(centroid, zoomLevel) {
return `${POTLATCH2_EDITOR_URL}#map=${[
zoomLevel,
roundToDecimals(centroid[1], 5),
roundToDecimals(centroid[0], 5),
].join('/')}`;
}
export function getIdUrl(project, centroid, zoomLevel, selectedTasks, customUrl) {
const base = customUrl ? formatCustomUrl(customUrl) : `${ID_EDITOR_URL}`;
let url = base + '#map=' + [zoomLevel, centroid[1], centroid[0]].join('/');
// the other URL params are only needed by external iD editors
if (!['?editor=ID', '?editor=RAPID'].includes(customUrl)) {
if (project.changesetComment) {
url += '&comment=' + encodeURIComponent(project.changesetComment);
}
if (project.imagery) {
if (project.imagery.includes('http')) {
url += '&background=custom:' + encodeURIComponent(formatImageryUrl(project.imagery));
} else {
url += '&background=' + encodeURIComponent(formatImageryUrl(project.imagery));
}
}
// add GPX
if (project.projectId && selectedTasks) {
url += '&gpx=' + encodeURIComponent(getTaskGpxUrl(project.projectId, selectedTasks));
}
if (project.idPresets) {
url += '&presets=' + encodeURIComponent(project.idPresets.join(','));
}
}
return url;
}
export const sendJosmCommands = async (project, tasks, selectedTasks, windowSize, taskBbox) => {
await loadTasksBoundaries(project, selectedTasks);
await loadImageryonJosm(project);
await selectedTasks.map(
async (task, n) =>
await loadOsmDataToTasks(
project,
taskBbox ? taskBbox : getSelectedTasksBBox(tasks, [task]),
n === 0 ? true : false,
),
);
return true;
};
// creates a new layer on JOSM and then add the tasks boundaries
function loadTasksBoundaries(project, selectedTasks) {
const layerName = `Boundary for task${selectedTasks.length > 1 ? 's:' : ':'} ${selectedTasks.join(
',',
)} of TM Project #${project.projectId} - Do not edit or upload`;
const emptyTaskLayerParams = {
new_layer: true,
layer_name: layerName,
data: '<?xml version="1.0" encoding="utf8"?><osm generator="JOSM" upload="never" version="0.6"></osm>',
};
const tmTaskLayerParams = {
new_layer: false,
url: getTaskXmlUrl(project.projectId, selectedTasks).href,
};
return callJosmRemoteControl(formatJosmUrl('load_data', emptyTaskLayerParams)).then((result) =>
callJosmRemoteControl(formatJosmUrl('import', tmTaskLayerParams)),
);
}
export function getImageryInfo(url) {
const type = url.toLowerCase().substr(0, 3) === 'wms' ? 'wms' : 'tms';
const zoom = url.substr(url.indexOf('[') + 1, url.indexOf(']') - url.indexOf('[') - 1);
const [minZoom, maxZoom] = zoom.length
? zoom.indexOf(':') !== -1
? zoom.split(':')
: [null, zoom]
: [null, null];
return [
type,
minZoom !== '' && minZoom !== null ? Number(minZoom) : null,
maxZoom !== '' && maxZoom !== null ? Number(maxZoom) : null,
];
}
function loadImageryonJosm(project) {
if (project.imagery) {
if (project.imagery.includes('http')) {
const [type, minZoom, maxZoom] = getImageryInfo(project.imagery);
const imageryParams = {
title: project.imagery,
type: type,
};
if (minZoom) imageryParams.min_zoom = minZoom;
if (maxZoom) imageryParams.max_zoom = maxZoom;
imageryParams.url = project.imagery.substr(project.imagery.indexOf('http'));
return callJosmRemoteControl(formatJosmUrl('imagery', imageryParams));
} else {
return callJosmRemoteControl(formatJosmUrl('imagery', { id: project.imagery }));
}
}
}
function loadOsmDataToTasks(project, bbox, newLayer) {
const emptyOSMLayerParams = {
new_layer: newLayer,
layer_name: 'OSM Data',
data: '<?xml version="1.0" encoding="utf8"?><osm generator="JOSM" version="0.6"></osm>',
};
const loadAndZoomParams = {
left: bbox[0],
bottom: bbox[1],
right: bbox[2],
top: bbox[3],
changeset_comment: project.changesetComment,
changeset_source: project.imagery ? project.imagery : '',
new_layer: false,
};
return callJosmRemoteControl(formatJosmUrl('load_data', emptyOSMLayerParams)).then((result) => {
return callJosmRemoteControl(formatJosmUrl('load_and_zoom', loadAndZoomParams));
});
}
export function formatJosmUrl(endpoint, params) {
return new URL(formatUrlParams(params), `http://127.0.0.1:8111/${endpoint}`);
}
export function formatCustomUrl(url) {
return url.includes('?') ? url : `${url}?`;
}
function roundToDecimals(input, decimals) {
const p = Math.pow(10, decimals);
return Math.round(input * p) / p;
}
export function formatUrlParams(params) {
const urlParams = Object.keys(params)
.map((key) => `${key}=${encodeURIComponent(params[key])}`)
.join('&');
return `?${urlParams}`;
}
let safariWindowReference = null;
const callJosmRemoteControl = function (uri) {
// Safari won't send AJAX commands to the default (insecure) JOSM port when
// on a secure site, and the secure JOSM port uses a self-signed certificate
// that requires the user to jump through a bunch of hoops to trust before
// communication can proceed. So for Safari only, fall back to sending JOSM
// requests via the opening of a separate window instead of AJAX.
// Source: https://github.com/osmlab/maproulette3
if (window.safari) {
return new Promise((resolve, reject) => {
if (safariWindowReference && !safariWindowReference.closed) {
safariWindowReference.close();
}
safariWindowReference = window.open(uri);
// Close the window after 1 second and resolve the promise
setTimeout(() => {
if (safariWindowReference && !safariWindowReference.closed) {
safariWindowReference.close();
}
resolve(true);
}, 1000);
});
}
return fetch(uri)
.then((response) => response.status === 200)
.catch((error) => {
console.log(error);
return false;
});
};