Skip to content

Commit

Permalink
fix (map-generation, ui): avoid selecting invalid bboxes
Browse files Browse the repository at this point in the history
- validate bbox and do not allow sending form if bbox crosses the antimeridian
- notify user with warning and display antimeridian if it is in the selected bbox

Refs: #279
  • Loading branch information
mcauer committed Oct 11, 2023
1 parent 0c7918c commit a27f918
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 23 deletions.
13 changes: 11 additions & 2 deletions client-src/base/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,15 @@ body {
display: inline-block;
}

.infobox {
.infobox,
.warningbox {
position: relative;
padding-left: 3.5rem;
margin: 0.5rem 0;
}

.infobox::before {
.infobox::before,
.warningbox::before {
content: "i";
position: absolute;
width: 2rem;
Expand All @@ -101,6 +104,12 @@ body {
top: calc(50% - 1rem);
}

.warningbox::before {
content: "!";
background-color: var(--heigit-red);
color: white;
}

.github-icon {
display: inline-block;
position: relative;
Expand Down
47 changes: 32 additions & 15 deletions client-src/create/form.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { PAPER_FORMAT, ORIENTATION, Margin } from "@giscience/ol-print-layout-control";
import {
toLonLat, get as getProjection, transformExtent,
} from "ol/proj";
import { Margin, ORIENTATION, PAPER_FORMAT } from "@giscience/ol-print-layout-control";
import { get as getProjection, toLonLat, transformExtent } from "ol/proj";
import { SKETCH_MAP_MARGINS } from "./sketchMapMargins";
import { fillSelectOptions, setDisabled } from "../shared";
import { fillSelectOptions } from "../shared";

function bindFormToPrintLayoutControl(printLayoutControl) {
function bindFormToPrintLayoutControl(printLayoutControl, messageController) {
const paperFormats = { ...PAPER_FORMAT };
delete paperFormats.BROADSHEET;

Expand Down Expand Up @@ -81,15 +79,31 @@ function bindFormToPrintLayoutControl(printLayoutControl) {
// disable form submit and display info if zoom is lower than 9
function handleZoomChange(zoom) {
if (zoom < 9) {
setDisabled("next-button", true);
document.querySelector("#infobox")
.classList
.remove("invisible");
messageController.addWarning("zoom-info");
} else {
setDisabled("next-button", false);
document.querySelector("#infobox")
.classList
.add("invisible");
messageController.removeWarning("zoom-info");
}
}

function handleAntimeridian(bboxWgs84) {
// normalizeLon uses mathematic modulo like in R, not % symetric modulo like in Java
// or Javascript
const normalizeLon = (x) => ((((x + 180) % 360) + 360) % 360) - 180;

const antimeridianLayer = printLayoutControl.getMap()
.getLayers().getArray()
.find((l) => l.get("name") === "Antimeridian");

if (!bboxWgs84) return;
// check if antimeridian is within extent -> when left (x1) is bigger than right (x2)
const [left, , right] = bboxWgs84;

if (normalizeLon(left) > normalizeLon(right)) {
messageController.addWarning("antimeridian-info");
antimeridianLayer.setVisible(true);
} else {
messageController.removeWarning("antimeridian-info");
antimeridianLayer.setVisible(false);
}
}

Expand All @@ -98,6 +112,7 @@ function bindFormToPrintLayoutControl(printLayoutControl) {
.getView();
const initialZoom = view.getZoom();
handleZoomChange(initialZoom);
handleAntimeridian(printLayoutControl.getBboxAsLonLat());

// update form state on zoomchange
printLayoutControl.getMap()
Expand All @@ -114,10 +129,12 @@ function bindFormToPrintLayoutControl(printLayoutControl) {
document.querySelector("#page-setup-form").submit();
});

// update the URL when the selection is changed (e.g. to bookmark the current selection)
printLayoutControl.on("change:bbox", (event) => {
// update the URL when the selection is changed (e.g. to bookmark the current selection)
const newCenter = printLayoutControl.getMap().getView().getCenter();
window.history.replaceState({}, document.title, `?center=${newCenter}`);
// show warning and disable form if bbox crosses the antimeridian
handleAntimeridian(event.target.getBboxAsLonLat());
});
}

Expand Down
7 changes: 5 additions & 2 deletions client-src/create/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import "@kirtandesai/ol-geocoder/dist/ol-geocoder.css";
import "./geocoder.css";
import "./create.css";

import { createMap, addPrintLayoutControl, addGeocoderControl } from "./map.js";
import { addGeocoderControl, addPrintLayoutControl, createMap } from "./map.js";
import { bindFormToPrintLayoutControl } from "./form.js";
import { MessageController } from "./messageController";

// Retrieve potentially given map center from URL (e.g. from a bookmarked selection)
const searchParams = new URLSearchParams(window.location.search);
Expand All @@ -20,5 +21,7 @@ if (centerArg != null) {
const map = createMap("map", center, 15);

const printLayoutControl = addPrintLayoutControl(map);
bindFormToPrintLayoutControl(printLayoutControl);
const messageController = new MessageController();

bindFormToPrintLayoutControl(printLayoutControl, messageController);
addGeocoderControl(map);
46 changes: 45 additions & 1 deletion client-src/create/map.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,53 @@
import { Map, View } from "ol";
import { Feature, Map, View } from "ol";
import { Tile } from "ol/layer";
import { OSM } from "ol/source";
import Geocoder from "@kirtandesai/ol-geocoder";
import { PrintLayout, PAPER_FORMAT, ORIENTATION } from "@giscience/ol-print-layout-control";
import { fromLonLat } from "ol/proj";
import { LineString } from "ol/geom";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import {
Fill, Stroke, Style, Text,
} from "ol/style";
import { SKETCH_MAP_MARGINS } from "./sketchMapMargins.js";

function createAntiMeridianLayer() {
// Create a LineString feature
const lineString = new LineString([
fromLonLat([180, -90]), // Start point just to the east of the antimeridian
fromLonLat([180, 90]), // End point just to the west of the antimeridian
]);

// Create a vector source and add the LineString feature to it
const vectorSource = new VectorSource({
features: [new Feature({
geometry: lineString,
})],
});

// Create a vector layer and set the source
return new VectorLayer({
name: "Antimeridian",
source: vectorSource,
visible: false,
style: new Style({
stroke: new Stroke({
color: "red",
width: 3,
}),
text: new Text({
text: "Antimeridian",
placement: "line",
offsetY: -10,
fill: new Fill({ color: "red" }),
font: "20px sans-serif",
repeat: 200,
}),
}),
});
}

/**
* Creates an OpenLayers Map to an element
* @param {string} [target=map] - a div id where the map will be rendered
Expand All @@ -26,6 +69,7 @@ function createMap(target = "map", lonLat = [966253.1800856147, 6344703.99262965
new Tile({
source: new OSM(),
}),
createAntiMeridianLayer(),
],
});

Expand Down
28 changes: 28 additions & 0 deletions client-src/create/messageController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { setDisabled } from "../shared";

export class MessageController {
constructor() {
this.activeMessageIds = new Set();
}

addWarning(elementId) {
if (this.activeMessageIds.has(elementId)) return;

setDisabled("next-button", true);
document.querySelector(`#${elementId}`)
.classList
.remove("hidden");

this.activeMessageIds.add(elementId);
}

removeWarning(elementId) {
this.activeMessageIds.delete(elementId);
document.querySelector(`#${elementId}`)
.classList
.add("hidden");
if (this.activeMessageIds.size === 0) {
setDisabled("next-button", false);
}
}
}
15 changes: 12 additions & 3 deletions sketch_map_tool/templates/create.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,18 @@

<div id="right">
<div class="sketchy h1 center-text">Page Setup</div>
<article id="infobox" class="infobox invisible">Your Area of Interest is too large!
<br>Zoom further in to be able to create a Sketch Map.
</article>
<div id="message-container" class="" style="height: 11rem; overflow-y: scroll; padding: 1rem; ">
<article id="zoom-info" class="infobox hidden">Your Area of Interest is too large!
<br>Zoom further in to be able to create a Sketch Map.
</article>
<article id="antimeridian-info" class="infobox">You are currently trying to
request a map that crosses the +/-180° <a href="https://en.wikipedia.org/wiki/180th_meridian" target="_blank">antimeridian</a>. This is currently not
supported, please move the map such that your extent is either left or right of
the antimeridian.
</article>
</div>


<form id="page-setup-form" action="/create/results" method="post">
<label for="format">Paper Format</label>
<select id="format" name="format"></select>
Expand Down

0 comments on commit a27f918

Please sign in to comment.