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

v.dissolve: Compute attribute aggregate statistics #2388

Merged
merged 24 commits into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e41d400
v.dissolve: Compute attribute aggregate statistics
wenzeslaus May 20, 2022
65c833d
Create columns only for the first value, support null (not clear how …
wenzeslaus Jun 2, 2022
0b34030
More tests, fix optional aggregation and multiple column creation
wenzeslaus Jun 3, 2022
ea60dde
Create and update all columns at once
wenzeslaus Jun 7, 2022
8ba12f6
Create transaction in a function
wenzeslaus Jun 7, 2022
2b84604
Support direct SQL as a backend besides v.db.univar. Handle cleanup f…
wenzeslaus Jun 8, 2022
504595f
Functions for common aggregation functionality; shorter code. Simplif…
wenzeslaus Jun 9, 2022
f7322ba
Test for dissolve/merge of areas without shared boundaries.
wenzeslaus Jun 9, 2022
4da6a56
Support any layer, not just 1
wenzeslaus Jun 9, 2022
d91a932
Check for valid methods more clear with ints than bools
wenzeslaus Jun 9, 2022
ec6ff98
Notebook with examples and test
wenzeslaus Jun 10, 2022
1fee2b0
Do not generate all combinations when all options are provided, add d…
wenzeslaus Jun 13, 2022
f3f0f84
Partially modernize the existing code by using gs alias instead of gr…
wenzeslaus Jun 13, 2022
415e666
Image to doc, generated in notebook
wenzeslaus Jun 13, 2022
588df54
Result of in is bool, no need to convert
wenzeslaus Jun 13, 2022
c7f26bd
Remove duplicate columns and methods for non-explicit automatic (inte…
wenzeslaus Jun 14, 2022
98a847d
Support SQL expressions as columns (as in v.db.update query_column or…
wenzeslaus Jun 15, 2022
18786dd
Support also text-returning aggregate function such as SQLite group_c…
wenzeslaus Jun 20, 2022
fbc0740
Add examples to documentation, use plural for columns and methods
wenzeslaus Jul 17, 2023
c880175
Add simple SQL escape function to double single quotes
wenzeslaus Jul 17, 2023
1d5dfee
Flip some conditions for better readability and less indentation
wenzeslaus Jul 17, 2023
10818e2
Improve author lists
wenzeslaus Jul 18, 2023
be56efd
Support general SQL syntax just like v.db.select for the price of les…
wenzeslaus Jul 18, 2023
9dc9867
Add more description and see also
wenzeslaus Jul 19, 2023
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
248 changes: 248 additions & 0 deletions scripts/v.dissolve/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
"""Fixtures for v.dissolve tests"""

from types import SimpleNamespace

import pytest

import grass.script as gs
import grass.script.setup as grass_setup


def updates_as_transaction(table, cat_column, column, column_quote, cats, values):
"""Create SQL statement for categories and values for a given column"""
sql = ["BEGIN TRANSACTION"]
if column_quote:
quote = "'"
else:
quote = ""
for cat, value in zip(cats, values):
sql.append(
f"UPDATE {table} SET {column} = {quote}{value}{quote} "
f"WHERE {cat_column} = {cat};"
)
sql.append("END TRANSACTION")
return "\n".join(sql)


def value_update_by_category(map_name, layer, column_name, cats, values):
"""Update column value for multiple rows based on category"""
db_info = gs.vector_db(map_name)[layer]
table = db_info["table"]
database = db_info["database"]
driver = db_info["driver"]
cat_column = "cat"
column_type = gs.vector_columns(map_name, layer)[column_name]
column_quote = bool(column_type["type"] in ("CHARACTER", "TEXT"))
sql = updates_as_transaction(
table=table,
cat_column=cat_column,
column=column_name,
column_quote=column_quote,
cats=cats,
values=values,
)
gs.write_command(
"db.execute", input="-", database=database, driver=driver, stdin=sql
)


@pytest.fixture(scope="module")
def dataset(tmp_path_factory):
"""Creates a session with a mapset which has vector with a float column"""
tmp_path = tmp_path_factory.mktemp("dataset")
location = "test"
point_map_name = "points"
map_name = "areas"
int_column_name = "int_value"
float_column_name = "double_value"
str_column_name = "str_value"

cats = [1, 2, 3, 4, 5, 6]
int_values = [10, 10, 10, 5, 24, 5]
float_values = [100.78, 102.78, 109.78, 104.78, 103.78, 105.78]
str_values = ["apples", "oranges", "oranges", "plumbs", "oranges", "plumbs"]
num_points = len(cats)

gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access
with grass_setup.init(tmp_path / location):
gs.run_command("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10)
gs.run_command("v.random", output=point_map_name, npoints=num_points, seed=42)
gs.run_command("v.voronoi", input=point_map_name, output=map_name)
gs.run_command(
"v.db.addtable",
map=map_name,
columns=[
f"{int_column_name} integer",
f"{float_column_name} double precision",
f"{str_column_name} text",
],
)
value_update_by_category(
map_name=map_name,
layer=1,
column_name=int_column_name,
cats=cats,
values=int_values,
)
value_update_by_category(
map_name=map_name,
layer=1,
column_name=float_column_name,
cats=cats,
values=float_values,
)
value_update_by_category(
map_name=map_name,
layer=1,
column_name=str_column_name,
cats=cats,
values=str_values,
)
yield SimpleNamespace(
vector_name=map_name,
int_column_name=int_column_name,
int_values=int_values,
float_column_name=float_column_name,
float_values=float_values,
str_column_name=str_column_name,
str_column_values=str_values,
)


@pytest.fixture(scope="module")
def discontinuous_dataset(tmp_path_factory):
"""Creates a session with a mapset which has vector with a float column"""
tmp_path = tmp_path_factory.mktemp("discontinuous_dataset")
location = "test"
point_map_name = "points"
map_name = "areas"
int_column_name = "int_value"
float_column_name = "double_value"
str_column_name = "str_value"

cats = [1, 2, 3, 4, 5, 6]
int_values = [10, 12, 10, 5, 24, 24]
float_values = [100.78, 102.78, 109.78, 104.78, 103.78, 105.78]
str_values = ["apples", "plumbs", "apples", "plumbs", "oranges", "oranges"]
num_points = len(cats)

gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access
with grass_setup.init(tmp_path / location):
gs.run_command("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10)
gs.run_command("v.random", output=point_map_name, npoints=num_points, seed=42)
gs.run_command("v.voronoi", input=point_map_name, output=map_name)
gs.run_command(
"v.db.addtable",
map=map_name,
columns=[
f"{int_column_name} integer",
f"{float_column_name} double precision",
f"{str_column_name} text",
],
)
value_update_by_category(
map_name=map_name,
layer=1,
column_name=int_column_name,
cats=cats,
values=int_values,
)
value_update_by_category(
map_name=map_name,
layer=1,
column_name=float_column_name,
cats=cats,
values=float_values,
)
value_update_by_category(
map_name=map_name,
layer=1,
column_name=str_column_name,
cats=cats,
values=str_values,
)
yield SimpleNamespace(
vector_name=map_name,
int_column_name=int_column_name,
int_values=int_values,
float_column_name=float_column_name,
float_values=float_values,
str_column_name=str_column_name,
str_column_values=str_values,
)


@pytest.fixture(scope="module")
def dataset_layer_2(tmp_path_factory):
"""Creates a session with a mapset which has vector with a float column"""
tmp_path = tmp_path_factory.mktemp("dataset_layer_2")
location = "test"
point_map_name = "points"
point_map_name_layer_2 = "points2"
map_name = "areas"
int_column_name = "int_value"
float_column_name = "double_value"
str_column_name = "str_value"

cats = [1, 2, 3, 4, 5, 6]
int_values = [10, 10, 10, 5, 24, 5]
float_values = [100.78, 102.78, 109.78, 104.78, 103.78, 105.78]
str_values = ["apples", "oranges", "oranges", "plumbs", "oranges", "plumbs"]
num_points = len(cats)

layer = 2

gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access
with grass_setup.init(tmp_path / location):
gs.run_command("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10)
gs.run_command("v.random", output=point_map_name, npoints=num_points, seed=42)
gs.run_command(
"v.category",
input=point_map_name,
layer=[1, layer],
output=point_map_name_layer_2,
option="transfer",
)
gs.run_command(
"v.voronoi", input=point_map_name_layer_2, layer=layer, output=map_name
)
gs.run_command(
"v.db.addtable",
map=map_name,
layer=layer,
columns=[
f"{int_column_name} integer",
f"{float_column_name} double precision",
f"{str_column_name} text",
],
)
value_update_by_category(
map_name=map_name,
layer=layer,
column_name=int_column_name,
cats=cats,
values=int_values,
)
value_update_by_category(
map_name=map_name,
layer=layer,
column_name=float_column_name,
cats=cats,
values=float_values,
)
value_update_by_category(
map_name=map_name,
layer=layer,
column_name=str_column_name,
cats=cats,
values=str_values,
)
yield SimpleNamespace(
vector_name=map_name,
int_column_name=int_column_name,
int_values=int_values,
float_column_name=float_column_name,
float_values=float_values,
str_column_name=str_column_name,
str_column_values=str_values,
)
Loading