Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ohmg/conf/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ def user_info_from_request(request):
user_info["api_keys"] = user.api_keys
user_info["is_authenticated"] = True
user_info["is_staff"] = user.is_staff
user_info["perms"] = list(user.get_all_permissions())
print(json.dumps(user_info, indent=1))
else:
user_info = {
"is_authenticated": False,
"is_staff": False,
"perms": [],
}
return user_info

Expand Down
24 changes: 24 additions & 0 deletions ohmg/core/migrations/0013_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.27 on 2026-06-05 09:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0012_layerset_xyz_tiles_prefix'),
]

operations = [
migrations.CreateModel(
name='Permissions',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'permissions': (('use_helmert', 'Can use Helmert transformation'),),
'managed': False,
'default_permissions': (),
},
),
]
1 change: 1 addition & 0 deletions ohmg/core/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from .layerset import LayerSet, LayerSetCategory # noqa: F401
from .map import Map # noqa: F401
from .mapgroup import MapGroup # noqa: F401
from .permissions import Permissions # noqa: F401
from .region import Region, RegionCategory # noqa: F401
19 changes: 19 additions & 0 deletions ohmg/core/models/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Source - https://stackoverflow.com/a/37988537
# Posted by Dmitry, modified by community. See post 'Timeline' for change history
# Retrieved 2026-06-05, License - CC BY-SA 4.0

from django.db import models


class Permissions(models.Model):
"""Permissions model for holding custom Django permissions objects
that are not tied to any specific model."""

class Meta:
managed = False # No database table creation or deletion \
# operations will be performed for this model.

default_permissions = () # disable "add", "change", "delete"
# and "view" default permissions

permissions = (("use_helmert", "Can use Helmert transformation"),)
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
import InfoModalButton from './buttons/InfoModalButton.svelte';
import SigninReminder from './common/SigninReminder.svelte';

import ConfirmModal from './modals/ConfirmModal.svelte';

export let CONTEXT;
export let REGION;
export let MAP;
Expand Down Expand Up @@ -119,7 +121,7 @@
leaveOkay = false;
enableButtons = true;
}
$: enableSave = gcpList.length >= 3 && enableButtons;
$: enableSave = gcpList.length >= minGCPs && enableButtons;

let countdown = 10;
let timer;
Expand Down Expand Up @@ -150,10 +152,42 @@
const noteInputElId = 'note-input';

let currentTransformation = 'poly1';
const transformations = [
{ id: 'poly1', name: 'Polynomial' },
{ id: 'tps', name: 'Thin Plate Spline' },
let minGCPs = 3;
$: transformations = [
{
id: 'poly1',
name: 'Polynomial',
enabled: true,
available: true
},
{
id: 'tps',
name: 'Thin Plate Spline',
enabled: true,
available: true
},
{
id: 'helmert',
name: 'Helmert 4-param',
enabled: gcpList.length == 2,
available: CONTEXT.user.perms.includes("core.use_helmert")
},
];
$: {
if (gcpList.length == 3 && currentTransformation == "helmert") {
currentTransformation = "poly1"
}
if (currentTransformation == "helmert") {
minGCPs = 2;
} else {
minGCPs = 3;
}
}
$: {
if (gcpList.length <= 2 && currentTransformation != "helmert") {
previewMode = "n/a"
}
}

let currentTargetProjection = 'EPSG:3857';
const availableProjections = [
Expand Down Expand Up @@ -703,7 +737,12 @@
};

function getPreview() {
if (gcpList.length < 3) {
if (currentTransformation == "helmert") {
minGCPs = 2;
} else {
minGCPs = 3;
}
if (gcpList.length < minGCPs) {
previewMode = 'n/a';
return;
}
Expand All @@ -729,7 +768,7 @@
}

function submitSession() {
if (gcpList.length < 3) {
if (gcpList.length < minGCPs) {
previewMode = 'n/a';
return;
}
Expand Down Expand Up @@ -869,6 +908,12 @@
>
</div>

<ConfirmModal id="modal-submit-helmert" yesAction={submitSession}>
<p>
You have only two GCPs, but it is highly advisable to have three or more.
Please add more, unless you are unable to do so.
</p>
</ConfirmModal>
<Modal id="modal-anonymous">
<SigninReminder next={CONTEXT.path} msg="Without an account you can experiment with the interface, but cannot submit your work."/>
</Modal>
Expand Down Expand Up @@ -944,7 +989,13 @@
<label><input type="checkbox" bind:checked={syncPanelWidth} /> autosize</label>
</div>
<div class="control-btn-group">
<ToolUIButton action={submitSession} title="Save control points" disabled={!enableSave}><Check /></ToolUIButton>
<ToolUIButton action={() => {
if (gcpList.length == 2) {
getModal('modal-submit-helmert').open()
} else {
submitSession()
}
}} title="Save control points" disabled={!enableSave}><Check /></ToolUIButton>
<ToolUIButton
action={() => {
getModal('modal-cancel').open();
Expand Down Expand Up @@ -1010,7 +1061,9 @@
Transformation:
<select class="trans-select" style="width:151px;" bind:value={currentTransformation} on:change={getPreview}>
{#each transformations as trans}
<option value={trans.id}>{trans.name}</option>
{#if trans.available}
<option value={trans.id} disabled={!trans.enabled}>{trans.name}</option>
{/if}
{/each}
</select>
</label>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script>
import Modal, { getModal } from '../base/Modal.svelte';

export let id;
export let yesButtonText = "Yes"
export let noButtonText = "No"
export let yesAction = () => {};
export let noAction = () => {};

</script>

<Modal id={id} closable={false}>
<slot>
<p>"Confirm this action?"</p>
</slot>
<button
class="button is-success"
title={yesButtonText}
on:click={() => {
yesAction();
getModal(id).close();
}}>{yesButtonText}</button
>
<button
class="button is-danger"
title={noButtonText}
on:click={() => {
noAction()
getModal(id).close();
}}>{noButtonText}</button
>
</Modal>
63 changes: 63 additions & 0 deletions ohmg/georeference/geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import math
from typing import Tuple

from django.contrib.gis.geos import LineString


def angle_from_coords(pt1: Tuple[float, float], pt2: Tuple[float, float]) -> float:
"""
Calculates the absolute Cartesian angle (in degrees) of the vector
pointing from p1 to p2, relative to the positive Y-axis.
"""

# Calculate differences
dx = pt2[0] - pt1[0]
dy = pt2[1] - pt1[1]

# Calculate angle in radians (-pi to pi)
radians = math.atan2(dx, dy)

degrees = math.degrees(radians)

return degrees


def extend_vector(
p1: Tuple[float, float],
p2: Tuple[float, float],
distance: float,
) -> Tuple[float, float]:
"""https://math.stackexchange.com/a/3346108 (credit to Oliver Roche)
takes the two input points, which represent a vector, and creates a
third point that would extend that vector by the given distance."""

x1, y1 = p1
x2, y2 = p2
rise = y2 - y1
run = x2 - x1

norm = math.sqrt((run**2) + (rise**2))

# if negative coords are used norm will be 0.0, silently return original point
if norm == 0.0:
return (x2, y2)

x3 = x2 + distance * (run / norm)
y3 = y2 + distance * (rise / norm)

return (x3, y3)


def extend_linestring(linestring: LineString, distance: int = 10) -> LineString:
"""takes the input GEOS LineString and extends it in both directions
(following the trajectory of each end segment) by the given distance."""

coord_list = list(linestring.coords)

new_start = extend_vector(coord_list[1], coord_list[0], distance)
new_end = extend_vector(coord_list[-2], coord_list[-1], distance)

coord_list.insert(0, new_start)
coord_list.append(new_end)

return LineString(coord_list)
Loading