Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates new Layer for project areas, filtered by scenario #1693

Merged
merged 3 commits into from
Aug 22, 2024
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
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ cypress-test:

migrate:
cd src/planscape && python3 manage.py migrate --no-input
cd src/planscape && python3 manage.py install_layers

load-conditions:
cd src/planscape && python3 manage.py load_conditions
Expand Down Expand Up @@ -149,6 +150,7 @@ docker-makemigrations:

docker-migrate:
./src/planscape/bin/run.sh python manage.py migrate
./src/planscape/bin/run.sh python manage.py install_layers

.PHONY: all docker-build docker-test docker-run docker-shell docker-makemigrations docker-migrate

71 changes: 24 additions & 47 deletions martin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,35 @@ max_feature_count: 500
postgres:
connection_string: ${DATABASE_URL}
functions:
treatment_plan_prescriptions:
# layer with a series of stands, given a treatment_plan_id
# parameters:
# - treatment_plan_id: int
# - project_area_id: Optional[int]
stands_by_tx_plan:
schema: public
function: martin_get_treatment_plan_prescriptions
function: martin_stands_by_tx_plan
minzoom: 0
maxzoom: 20
bounds: [-180.0, -90.0, 180.0, 90.0]
project_area_outline:
# layer with the project area outline. This unions all stands
# that compose the project are and creates a single multi polygon.
# this layer changes the shape of the project area to conform to
# the underlying stands.
# parameters:
# - project_area_id: int
project_area_aggregate:
schema: public
function: martin_project_area_outline
function: martin_project_area_aggregate
minzoom: 0
maxzoom: 20
bounds: [-180.0, -90.0, 180.0, 90.0]
# layer with project areas. This layer shows original project
# areas, as imported or generated by any algorithm.
# parameters:
# - scenario_id: int
project_areas_by_scenario:
schema: public
function: martin_project_areas_by_scenario
minzoom: 0
maxzoom: 20
bounds: [-180.0, -90.0, 180.0, 90.0]
Expand All @@ -33,46 +53,3 @@ postgres:
properties:
id: int8
size: string
planning_areas:
layer_id: planning_areas
schema: public
table: planning_planningarea
srid: 4269
geometry_column: geometry
id_column: id
min_zoom: 0
max_zoom: 20
geometry_type: POLYGON
properties:
id: int8
name: string
project_areas:
layer_id: project_areas
schema: public
table: planning_projectarea
srid: 4269
geometry_column: geometry
id_column: id
minzoom: 0
maxzoom: 20
geometry_type: POLYGON
properties:
id: int8
name: string
origin: string
treatment_prescriptions:
layer_id: treatment_prescriptions
schema: public
table: impacts_treatmentprescription
srid: 4269
geometry_column: geometry
minzoom: 0
maxzoom: 20
geometry_type: POLYGON
properties:
id: int8
stand_size: string
treatment_plan_id: int8
project_area_id: int8
type: string
action: string
Empty file.
64 changes: 64 additions & 0 deletions src/planscape/martin/management/commands/install_layers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from pathlib import Path
from typing import List, Tuple, Union

from django.core.management.base import BaseCommand
from django.db import connection

from utils.file_utils import read_file


TInput = Union[Path, str]
TResult = Tuple[bool, TInput]


class Command(BaseCommand):
help = "Installs all custom plpgsql functions used to generate layers"

def add_arguments(self, parser) -> None:
parser.add_argument("--folder", type=str, default=None)
parser.add_argument("--uninstall", action="store_true", default=False)

def default_folder(self) -> Path:
return Path("martin/sql")

def get_uninstall_sql(self, file: TInput) -> str:
filepath = Path(file)
return f"DROP FUNCTION IF EXISTS {filepath.stem};"

def uninstall(self, file: TInput) -> TResult:
try:
with connection.cursor() as cursor:
cursor.execute(self.get_uninstall_sql(file))
return (True, file)
except Exception:
return (False, file)

def install(self, file: TInput) -> TResult:
try:
with connection.cursor() as cursor:
cursor.execute(file.read_text())
return (True, file)
except Exception:
return (False, file)

def handle(self, *args, **options):
folder = Path(
options.get("folder", self.default_folder()) or self.default_folder()
)
files = list(folder.glob("*.sql"))
self.stdout.write("The following layers were found:")
for f in files:
self.stdout.write(f"{f} found")
is_installation = not options.get("uninstall", False) or False
fn = self.install if is_installation else self.uninstall
results = list([fn(f) for f in files])
for result in results:
self.report_result(result, is_installation)

def report_result(self, result: TResult, is_installation: bool = False) -> None:
success, file = result
fn = self.stdout.write if success else self.stderr.write
indicator1 = "succeeded" if success else "failed"
indicator2 = "[OK]" if success else "[FAIL]"
verb = "installation" if is_installation else "deletion"
fn(f"{indicator2} {file} {verb} {indicator1}")
12 changes: 0 additions & 12 deletions src/planscape/martin/migrations/0001_initial.py

This file was deleted.

19 changes: 0 additions & 19 deletions src/planscape/martin/migrations/0002_auto_20240703_1654.py

This file was deleted.

16 changes: 0 additions & 16 deletions src/planscape/martin/migrations/0003_auto_20240704_1556.py

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION martin_project_area_outline(z integer, x integer, y integer, query_params json)
CREATE OR REPLACE FUNCTION martin_project_area_aggregate(z integer, x integer, y integer, query_params json)
RETURNS bytea AS $$
DECLARE
p_mvt bytea;
Expand All @@ -8,7 +8,12 @@ DECLARE
BEGIN

SELECT INTO p_project_area (
SELECT geometry FROM planning_projectarea WHERE id = (query_params->>'project_area_id')::int
SELECT
geometry
FROM planning_projectarea pa
WHERE
id = (query_params->>'project_area_id')::int AND
pa.deleted_at IS NULL
);

SELECT INTO p_stand_size (
Expand All @@ -27,7 +32,7 @@ BEGIN
ss.size = p_stand_size
);

SELECT INTO p_mvt ST_AsMVT(tile, 'project_area_outline', 4096, 'geom') FROM (
SELECT INTO p_mvt ST_AsMVT(tile, 'project_area_aggregate', 4096, 'geom') FROM (
SELECT
(query_params->>'project_area_id')::int as "id",
ST_AsMVTGeom(
Expand Down
28 changes: 28 additions & 0 deletions src/planscape/martin/sql/martin_project_areas_by_scenario.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
CREATE OR REPLACE FUNCTION martin_project_areas_by_scenario(z integer, x integer, y integer, query_params json)
RETURNS bytea AS $$
DECLARE
p_mvt bytea;
p_intersecting_area geometry;
p_stand_size varchar;
BEGIN

SELECT INTO p_mvt ST_AsMVT(tile, 'project_areas_by_scenario', 4096, 'geom') FROM (

SELECT
pa.id as "id",
pa.scenario_id as "scenario_id",
pa.name,
COALESCE(pa.data, '{}'::jsonb) ->> 'treatment_rank' as "rank",
ST_AsMVTGeom(
ST_Transform(pa.geometry, 3857),
ST_TileEnvelope(z, x, y),
4096, 64, true) AS geom
FROM stands_stand ss
WHERE
pa.deleted_at is NULL AND
pa.scenario_id = (query_params::jsonb)->>'scenario_id' AND
pa.geometry && ST_Transform(ST_TileEnvelope(z, x, y, margin => (64.0 / 4096)), 4269)
) as tile WHERE geom IS NOT NULL;

RETURN p_mvt;
END $$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
Loading
Loading