corelle.engine 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- corelle/engine/__init__.py +1 -0
- corelle/engine/cache.py +78 -0
- corelle/engine/cli.py +188 -0
- corelle/engine/database.py +35 -0
- corelle/engine/load_data.py +350 -0
- corelle/engine/query/__init__.py +7 -0
- corelle/engine/query/active-plates-at-time.sql +11 -0
- corelle/engine/query/cache-feature-dataset.sql +37 -0
- corelle/engine/query/delete-extraneous-cache.sql +7 -0
- corelle/engine/query/feature-dataset-cached.sql +4 -0
- corelle/engine/query/feature-dataset.sql +21 -0
- corelle/engine/query/modern-plate-polygons-cached.sql +3 -0
- corelle/engine/query/modern-plate-polygons.sql +14 -0
- corelle/engine/query/plate-for-point.sql +7 -0
- corelle/engine/query/plate-time-ranges.sql +9 -0
- corelle/engine/query/plates-for-model.sql +6 -0
- corelle/engine/query/rotation-pairs-for-model.sql +64 -0
- corelle/engine/query/rotation-pairs-for-time.sql +63 -0
- corelle/engine/query/rotation-pairs.sql +64 -0
- corelle/engine/query/update-cache.sql +9 -0
- corelle/engine/rotate.py +265 -0
- corelle/engine/schema/00-init.sql +2 -0
- corelle/engine/schema/01-schema.sql +88 -0
- corelle/engine/schema/02-functions.sql +233 -0
- corelle/engine/schema/03-testing-functions.sql +186 -0
- corelle/engine/schema/04-views.sql +47 -0
- corelle/engine/storage.py +11 -0
- corelle_engine-3.0.0.dist-info/METADATA +31 -0
- corelle_engine-3.0.0.dist-info/RECORD +31 -0
- corelle_engine-3.0.0.dist-info/WHEEL +4 -0
- corelle_engine-3.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
WITH a AS (
|
|
2
|
+
SELECT
|
|
3
|
+
model_id,
|
|
4
|
+
json_agg(json_build_object(
|
|
5
|
+
'id', f.id,
|
|
6
|
+
'properties', properties || jsonb_build_object(
|
|
7
|
+
'plate_id', plate_id,
|
|
8
|
+
'young_lim', coalesce(young_lim, m.min_age),
|
|
9
|
+
'old_lim', coalesce(old_lim, m.max_age)
|
|
10
|
+
),
|
|
11
|
+
'geometry', ST_AsGeoJSON(
|
|
12
|
+
ST_Intersection(
|
|
13
|
+
ST_Buffer(p.geometry,0),
|
|
14
|
+
ST_Buffer(f.geometry, 0)
|
|
15
|
+
)
|
|
16
|
+
)::jsonb,
|
|
17
|
+
'type', 'Feature'
|
|
18
|
+
)) geojson
|
|
19
|
+
FROM corelle.feature f
|
|
20
|
+
JOIN corelle.plate_polygon p
|
|
21
|
+
ON p.geometry && f.geometry
|
|
22
|
+
AND ST_Intersects(p.geometry, f.geometry)
|
|
23
|
+
JOIN corelle.model m
|
|
24
|
+
ON m.id = p.model_id
|
|
25
|
+
WHERE dataset_id = :dataset_id
|
|
26
|
+
GROUP BY model_id
|
|
27
|
+
)
|
|
28
|
+
INSERT INTO corelle.feature_cache (model_id, model_name, geojson, dataset_id)
|
|
29
|
+
SELECT
|
|
30
|
+
model_id,
|
|
31
|
+
(SELECT name FROM corelle.model WHERE id = model_id) AS model_name,
|
|
32
|
+
geojson,
|
|
33
|
+
:dataset_id dataset_id
|
|
34
|
+
FROM a
|
|
35
|
+
ON CONFLICT (model_id, dataset_id)
|
|
36
|
+
DO UPDATE SET
|
|
37
|
+
geojson = EXCLUDED.geojson;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
SELECT
|
|
2
|
+
f.id,
|
|
3
|
+
properties || jsonb_build_object(
|
|
4
|
+
'plate_id', plate_id,
|
|
5
|
+
'young_lim', coalesce(young_lim, m.min_age),
|
|
6
|
+
'old_lim', coalesce(old_lim, m.max_age)) properties,
|
|
7
|
+
ST_AsGeoJSON(
|
|
8
|
+
ST_Intersection(
|
|
9
|
+
ST_Buffer(p.geometry,0),
|
|
10
|
+
ST_Buffer(f.geometry, 0)
|
|
11
|
+
)
|
|
12
|
+
)::json geometry,
|
|
13
|
+
'Feature' "type"
|
|
14
|
+
FROM corelle.feature f
|
|
15
|
+
JOIN corelle.plate_polygon p
|
|
16
|
+
ON p.geometry && f.geometry
|
|
17
|
+
AND ST_Intersects(p.geometry, f.geometry)
|
|
18
|
+
JOIN corelle.model m
|
|
19
|
+
ON m.id = p.model_id
|
|
20
|
+
WHERE dataset_id = :dataset
|
|
21
|
+
AND model_id = (SELECT id FROM corelle.model WHERE name = :model_name)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
SELECT
|
|
2
|
+
pp.id,
|
|
3
|
+
pp.plate_id,
|
|
4
|
+
coalesce(young_lim, 0) young_lim,
|
|
5
|
+
old_lim,
|
|
6
|
+
ST_AsGeoJSON(geometry) geometry,
|
|
7
|
+
p.name
|
|
8
|
+
FROM corelle.plate_polygon pp
|
|
9
|
+
JOIN corelle.plate p
|
|
10
|
+
ON pp.plate_id = p.id
|
|
11
|
+
AND pp.model_id = p.model_id
|
|
12
|
+
JOIN corelle.model m
|
|
13
|
+
ON p.model_id = m.id
|
|
14
|
+
WHERE m.name = :model_name
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
WITH plate_steps AS (
|
|
2
|
+
/*
|
|
3
|
+
Rotations for a specific plate and model id
|
|
4
|
+
*/
|
|
5
|
+
SELECT
|
|
6
|
+
plate_id,
|
|
7
|
+
ref_plate_id,
|
|
8
|
+
metadata,
|
|
9
|
+
t_step,
|
|
10
|
+
ARRAY[longitude, latitude, angle] rotation,
|
|
11
|
+
lag(t_step, 1) OVER (
|
|
12
|
+
ORDER BY plate_id, ref_plate_id, t_step
|
|
13
|
+
) prev_step
|
|
14
|
+
FROM corelle.rotation
|
|
15
|
+
WHERE model_id = (
|
|
16
|
+
SELECT id
|
|
17
|
+
FROM corelle.model
|
|
18
|
+
WHERE name = :model_name)
|
|
19
|
+
ORDER BY t_step
|
|
20
|
+
),
|
|
21
|
+
step_pairs AS (
|
|
22
|
+
/*
|
|
23
|
+
Paired steps for interpolated rotations.
|
|
24
|
+
Unlike our previous attempt at the math, these
|
|
25
|
+
MUST have the same ref_plate_id, because this is
|
|
26
|
+
how GPlates is actually structured.
|
|
27
|
+
*/
|
|
28
|
+
SELECT
|
|
29
|
+
p1.plate_id,
|
|
30
|
+
p1.ref_plate_id,
|
|
31
|
+
p1.t_step r1_step,
|
|
32
|
+
p2.t_step r2_step,
|
|
33
|
+
p1.rotation r1_rotation,
|
|
34
|
+
p2.rotation r2_rotation,
|
|
35
|
+
p1.metadata r1_metadata,
|
|
36
|
+
p2.metadata r2_metadata
|
|
37
|
+
FROM plate_steps p1
|
|
38
|
+
JOIN plate_steps p2
|
|
39
|
+
ON p1.t_step = p2.prev_step
|
|
40
|
+
AND p1.ref_plate_id = p2.ref_plate_id
|
|
41
|
+
AND p1.plate_id = p2.plate_id
|
|
42
|
+
)
|
|
43
|
+
-- Get rotation that are exactly at `time`, if they exist
|
|
44
|
+
SELECT
|
|
45
|
+
plate_id,
|
|
46
|
+
ref_plate_id,
|
|
47
|
+
t_step r1_step,
|
|
48
|
+
null r2_step,
|
|
49
|
+
rotation r1_rotation,
|
|
50
|
+
null r2_rotation,
|
|
51
|
+
metadata r1_metadata,
|
|
52
|
+
null r2_metadata,
|
|
53
|
+
false interpolated
|
|
54
|
+
FROM plate_steps
|
|
55
|
+
WHERE t_step <= :early_age
|
|
56
|
+
AND t_step >= :late_age
|
|
57
|
+
UNION ALL
|
|
58
|
+
-- Get rotations that are after `time`
|
|
59
|
+
SELECT
|
|
60
|
+
*,
|
|
61
|
+
true interpolated
|
|
62
|
+
FROM step_pairs
|
|
63
|
+
WHERE r1_step <= :early_age
|
|
64
|
+
AND r2_step > :late_age
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
WITH plate_steps AS (
|
|
2
|
+
/*
|
|
3
|
+
Rotations for a specific plate and model id
|
|
4
|
+
*/
|
|
5
|
+
SELECT
|
|
6
|
+
plate_id,
|
|
7
|
+
ref_plate_id,
|
|
8
|
+
metadata,
|
|
9
|
+
t_step,
|
|
10
|
+
ARRAY[longitude, latitude, angle] rotation,
|
|
11
|
+
lag(t_step, 1) OVER (
|
|
12
|
+
ORDER BY plate_id, ref_plate_id, t_step
|
|
13
|
+
) prev_step
|
|
14
|
+
FROM corelle.rotation
|
|
15
|
+
WHERE model_id = (
|
|
16
|
+
SELECT id
|
|
17
|
+
FROM corelle.model
|
|
18
|
+
WHERE name = :model_name)
|
|
19
|
+
ORDER BY t_step
|
|
20
|
+
),
|
|
21
|
+
step_pairs AS (
|
|
22
|
+
/*
|
|
23
|
+
Paired steps for interpolated rotations.
|
|
24
|
+
Unlike our previous attempt at the math, these
|
|
25
|
+
MUST have the same ref_plate_id, because this is
|
|
26
|
+
how GPlates is actually structured.
|
|
27
|
+
*/
|
|
28
|
+
SELECT
|
|
29
|
+
p1.plate_id,
|
|
30
|
+
p1.ref_plate_id,
|
|
31
|
+
p1.t_step r1_step,
|
|
32
|
+
p2.t_step r2_step,
|
|
33
|
+
p1.rotation r1_rotation,
|
|
34
|
+
p2.rotation r2_rotation,
|
|
35
|
+
p1.metadata r1_metadata,
|
|
36
|
+
p2.metadata r2_metadata
|
|
37
|
+
FROM plate_steps p1
|
|
38
|
+
JOIN plate_steps p2
|
|
39
|
+
ON p1.t_step = p2.prev_step
|
|
40
|
+
AND p1.ref_plate_id = p2.ref_plate_id
|
|
41
|
+
AND p1.plate_id = p2.plate_id
|
|
42
|
+
)
|
|
43
|
+
-- Get rotation that are exactly at `time`, if they exist
|
|
44
|
+
SELECT
|
|
45
|
+
plate_id,
|
|
46
|
+
ref_plate_id,
|
|
47
|
+
t_step r1_step,
|
|
48
|
+
null r2_step,
|
|
49
|
+
rotation r1_rotation,
|
|
50
|
+
null r2_rotation,
|
|
51
|
+
metadata r1_metadata,
|
|
52
|
+
null r2_metadata,
|
|
53
|
+
false interpolated
|
|
54
|
+
FROM plate_steps
|
|
55
|
+
WHERE t_step = :time
|
|
56
|
+
UNION ALL
|
|
57
|
+
-- Get rotations that are after `time`
|
|
58
|
+
SELECT
|
|
59
|
+
*,
|
|
60
|
+
true interpolated
|
|
61
|
+
FROM step_pairs
|
|
62
|
+
WHERE r1_step < :time
|
|
63
|
+
AND r2_step > :time
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
WITH plate_steps AS (
|
|
2
|
+
/*
|
|
3
|
+
Rotations for a specific plate and model id
|
|
4
|
+
*/
|
|
5
|
+
SELECT
|
|
6
|
+
plate_id,
|
|
7
|
+
ref_plate_id,
|
|
8
|
+
metadata,
|
|
9
|
+
t_step,
|
|
10
|
+
ARRAY[longitude, latitude, angle] rotation,
|
|
11
|
+
lag(t_step, 1) OVER (
|
|
12
|
+
ORDER BY plate_id, ref_plate_id, t_step
|
|
13
|
+
) prev_step
|
|
14
|
+
FROM corelle.rotation
|
|
15
|
+
WHERE plate_id = :plate_id
|
|
16
|
+
AND model_id = (
|
|
17
|
+
SELECT id
|
|
18
|
+
FROM corelle.model
|
|
19
|
+
WHERE name = :model_name)
|
|
20
|
+
ORDER BY t_step
|
|
21
|
+
),
|
|
22
|
+
step_pairs AS (
|
|
23
|
+
/*
|
|
24
|
+
Paired steps for interpolated rotations.
|
|
25
|
+
Unlike our previous attempt at the math, these
|
|
26
|
+
MUST have the same ref_plate_id, because this is
|
|
27
|
+
how GPlates is actually structured.
|
|
28
|
+
*/
|
|
29
|
+
SELECT
|
|
30
|
+
p1.plate_id,
|
|
31
|
+
p1.ref_plate_id,
|
|
32
|
+
p1.t_step r1_step,
|
|
33
|
+
p2.t_step r2_step,
|
|
34
|
+
p1.rotation r1_rotation,
|
|
35
|
+
p2.rotation r2_rotation,
|
|
36
|
+
p1.metadata r1_metadata,
|
|
37
|
+
p2.metadata r2_metadata
|
|
38
|
+
FROM plate_steps p1
|
|
39
|
+
JOIN plate_steps p2
|
|
40
|
+
ON p1.t_step = p2.prev_step
|
|
41
|
+
AND p1.ref_plate_id = p2.ref_plate_id
|
|
42
|
+
AND p1.plate_id = p2.plate_id
|
|
43
|
+
)
|
|
44
|
+
-- Get rotation that are exactly at `time`, if they exist
|
|
45
|
+
SELECT
|
|
46
|
+
plate_id,
|
|
47
|
+
ref_plate_id,
|
|
48
|
+
t_step r1_step,
|
|
49
|
+
null r2_step,
|
|
50
|
+
rotation r1_rotation,
|
|
51
|
+
null r2_rotation,
|
|
52
|
+
metadata r1_metadata,
|
|
53
|
+
null r2_metadata,
|
|
54
|
+
false interpolated
|
|
55
|
+
FROM plate_steps
|
|
56
|
+
WHERE t_step = :time
|
|
57
|
+
UNION ALL
|
|
58
|
+
-- Get rotations that are after `time`
|
|
59
|
+
SELECT
|
|
60
|
+
*,
|
|
61
|
+
true interpolated
|
|
62
|
+
FROM step_pairs
|
|
63
|
+
WHERE r1_step < :time
|
|
64
|
+
AND r2_step > :time
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
UPDATE corelle.rotation_cache rc SET
|
|
2
|
+
proj4text = corelle.build_proj_string(rotation),
|
|
3
|
+
proj4inv = corelle.build_proj_string(corelle.invert_rotation(rotation)),
|
|
4
|
+
envelope = ST_Envelope(corelle.rotate_geometry(pp.geometry, rotation))
|
|
5
|
+
FROM corelle.plate_polygon pp
|
|
6
|
+
WHERE rc.model_id = :model_id
|
|
7
|
+
AND t_step = :t_step
|
|
8
|
+
AND rc.plate_id = pp.plate_id
|
|
9
|
+
AND rc.model_id = pp.model_id;
|
corelle/engine/rotate.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import numpy as N
|
|
2
|
+
import quaternion as Q
|
|
3
|
+
from click import secho
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
|
|
6
|
+
from corelle.math import cart2sph, sph2cart, euler_to_quaternion, quaternion_to_euler
|
|
7
|
+
|
|
8
|
+
from .query import get_sql
|
|
9
|
+
from .storage import model_id
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RotationError(Exception):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UndefinedRotationError(RotationError):
|
|
17
|
+
def __init__(self, plate_id, time):
|
|
18
|
+
super().__init__(self, f"No rotation found for plate {plate_id} at time {time}")
|
|
19
|
+
self.plate_id = plate_id
|
|
20
|
+
self.time = time
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
cache = {}
|
|
24
|
+
cache_list = []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def build_cache(q, cache_args):
|
|
28
|
+
# Builds an in-memory cache of rotations
|
|
29
|
+
cache[cache_args] = q
|
|
30
|
+
cache_list.append(cache_args)
|
|
31
|
+
if len(cache_list) > 50000:
|
|
32
|
+
id = cache_list.pop(0)
|
|
33
|
+
try:
|
|
34
|
+
del cache[id]
|
|
35
|
+
except KeyError:
|
|
36
|
+
pass
|
|
37
|
+
return q
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__sql = get_sql("rotation-pairs")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def check_model_id(conn, model_name):
|
|
44
|
+
# Make sure our model id actually exists
|
|
45
|
+
id = model_id(conn, model_name)
|
|
46
|
+
if id is None:
|
|
47
|
+
raise RotationError("Unknown model id")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Cache this expensive, recursive function.
|
|
51
|
+
def get_rotation(
|
|
52
|
+
conn,
|
|
53
|
+
model_name,
|
|
54
|
+
plate_id,
|
|
55
|
+
time,
|
|
56
|
+
verbose=False,
|
|
57
|
+
depth=0,
|
|
58
|
+
rowset=None,
|
|
59
|
+
safe=True,
|
|
60
|
+
):
|
|
61
|
+
"""Core function to rotate a plate to a time by accumulating quaternions"""
|
|
62
|
+
time = float(time)
|
|
63
|
+
cache_args = (model_name, plate_id, Decimal(time))
|
|
64
|
+
if cache_args in cache:
|
|
65
|
+
return cache[cache_args]
|
|
66
|
+
|
|
67
|
+
if safe:
|
|
68
|
+
check_model_id(conn, model_name)
|
|
69
|
+
|
|
70
|
+
__cache = lambda q: build_cache(q, cache_args)
|
|
71
|
+
|
|
72
|
+
prefix = " " * depth
|
|
73
|
+
if verbose:
|
|
74
|
+
secho(prefix + str(plate_id))
|
|
75
|
+
|
|
76
|
+
if plate_id is None or plate_id == 0:
|
|
77
|
+
return __cache(N.quaternion(1, 0, 0, 0))
|
|
78
|
+
|
|
79
|
+
params = dict(plate_id=plate_id, model_name=model_name, time=time)
|
|
80
|
+
|
|
81
|
+
row = None
|
|
82
|
+
pairs = []
|
|
83
|
+
if rowset:
|
|
84
|
+
for ix, p in enumerate(rowset):
|
|
85
|
+
if p.plate_id == plate_id and p.r1_step <= time:
|
|
86
|
+
pairs = [p]
|
|
87
|
+
# Remove this row from the rowset (prevents weird recursion errors)
|
|
88
|
+
rowset = rowset[:ix] + rowset[ix + 1 :]
|
|
89
|
+
break
|
|
90
|
+
else:
|
|
91
|
+
# Fall back to fetching the data ourselves
|
|
92
|
+
pairs = conn.execute(__sql, params).fetchall()
|
|
93
|
+
if len(pairs) == 0:
|
|
94
|
+
return __cache(None)
|
|
95
|
+
if verbose:
|
|
96
|
+
for i, pair in enumerate(pairs):
|
|
97
|
+
color = "green" if i == 0 else "white"
|
|
98
|
+
secho(prefix + f"{pair.plate_id} → {pair.ref_plate_id}", fg=color)
|
|
99
|
+
|
|
100
|
+
row = pairs[0]
|
|
101
|
+
|
|
102
|
+
q1 = euler_to_quaternion(row.r1_rotation)
|
|
103
|
+
|
|
104
|
+
if not row.interpolated:
|
|
105
|
+
base = get_rotation(
|
|
106
|
+
conn,
|
|
107
|
+
model_name,
|
|
108
|
+
row.ref_plate_id,
|
|
109
|
+
row.r1_step,
|
|
110
|
+
verbose=verbose,
|
|
111
|
+
depth=depth + 1,
|
|
112
|
+
rowset=rowset,
|
|
113
|
+
safe=False,
|
|
114
|
+
)
|
|
115
|
+
if base is None:
|
|
116
|
+
return __cache(None)
|
|
117
|
+
# We have an exact match!
|
|
118
|
+
# Just a precautionary guard, this should be assured by our SQL
|
|
119
|
+
return __cache(base * q1)
|
|
120
|
+
|
|
121
|
+
# Interpolated rotations
|
|
122
|
+
base = get_rotation(
|
|
123
|
+
conn,
|
|
124
|
+
model_name,
|
|
125
|
+
row.ref_plate_id,
|
|
126
|
+
time,
|
|
127
|
+
verbose=verbose,
|
|
128
|
+
depth=depth + 1,
|
|
129
|
+
rowset=rowset,
|
|
130
|
+
safe=False,
|
|
131
|
+
)
|
|
132
|
+
if base is None:
|
|
133
|
+
return __cache(None)
|
|
134
|
+
|
|
135
|
+
q2 = euler_to_quaternion(row.r2_rotation)
|
|
136
|
+
# Calculate interpolated rotation between the two timesteps
|
|
137
|
+
res = Q.slerp(q1, q2, float(row.r1_step), float(row.r2_step), time)
|
|
138
|
+
return __cache(base * res)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_plate_id(conn, point, model, time):
|
|
142
|
+
sql = get_sql("plate-for-point")
|
|
143
|
+
return conn.execute(
|
|
144
|
+
sql, dict(lon=point[0], lat=point[1], model_name=model, time=time)
|
|
145
|
+
).scalar()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def rotate_point(conn, point, model, time):
|
|
149
|
+
if time == 0:
|
|
150
|
+
return point
|
|
151
|
+
plate_id = get_plate_id(conn, point, model, time)
|
|
152
|
+
if plate_id is None:
|
|
153
|
+
return None
|
|
154
|
+
q = get_rotation(conn, model, plate_id, time)
|
|
155
|
+
v0 = sph2cart(*point)
|
|
156
|
+
v1 = Q.rotate_vectors(q, v0)
|
|
157
|
+
return cart2sph(v1)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def rotate_point_with_plate(conn, point, model, time):
|
|
161
|
+
"""The rotated point and its plate ID, or None for a point on no plate."""
|
|
162
|
+
plate_id = get_plate_id(conn, point, model, time)
|
|
163
|
+
if plate_id is None:
|
|
164
|
+
return None
|
|
165
|
+
q = get_rotation(conn, model, plate_id, time)
|
|
166
|
+
v1 = Q.rotate_vectors(q, sph2cart(*point))
|
|
167
|
+
return cart2sph(v1), plate_id
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
__tstep_rotation_pairs = get_sql("rotation-pairs-for-time")
|
|
171
|
+
__model_rotation_pairs = get_sql("rotation-pairs-for-model")
|
|
172
|
+
__active_plates_sql = get_sql("active-plates-at-time")
|
|
173
|
+
__model_plates_sql = get_sql("plates-for-model")
|
|
174
|
+
__plate_time_ranges = get_sql("plate-time-ranges")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def plates_for_model(conn, model):
|
|
178
|
+
"""Returns a list of plate IDs for a given model"""
|
|
179
|
+
return [k[0] for k in conn.execute(__model_plates_sql, dict(model_name=model))]
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def get_all_rotations(
|
|
183
|
+
conn,
|
|
184
|
+
model,
|
|
185
|
+
time,
|
|
186
|
+
verbose=False,
|
|
187
|
+
active_only=True,
|
|
188
|
+
plates=None,
|
|
189
|
+
safe=True,
|
|
190
|
+
rowset=None,
|
|
191
|
+
):
|
|
192
|
+
"""Get all rotations for a model and a timestep
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
model : str
|
|
197
|
+
The rotation model to use
|
|
198
|
+
time : float
|
|
199
|
+
The time to rotate to, in Myr before present.
|
|
200
|
+
|
|
201
|
+
Yields
|
|
202
|
+
------
|
|
203
|
+
dict
|
|
204
|
+
"""
|
|
205
|
+
if plates is None:
|
|
206
|
+
if active_only:
|
|
207
|
+
res = conn.execute(__active_plates_sql, dict(time=time, model_name=model))
|
|
208
|
+
plates = [p[0] for p in res]
|
|
209
|
+
else:
|
|
210
|
+
plates = plates_for_model(conn, model)
|
|
211
|
+
|
|
212
|
+
if rowset is None:
|
|
213
|
+
_rowset = conn.execute(
|
|
214
|
+
__tstep_rotation_pairs, dict(time=time, model_name=model)
|
|
215
|
+
).fetchall()
|
|
216
|
+
else:
|
|
217
|
+
_rowset = [
|
|
218
|
+
r
|
|
219
|
+
for r in rowset
|
|
220
|
+
if (r.r1_step == time and r.r2_step is None)
|
|
221
|
+
or (r.r1_step < time and (r.r2_step or -1) > time)
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
if safe:
|
|
225
|
+
check_model_id(conn, model)
|
|
226
|
+
for plate_id in plates:
|
|
227
|
+
q = get_rotation(
|
|
228
|
+
conn, model, plate_id, time, verbose=verbose, rowset=_rowset, safe=False
|
|
229
|
+
)
|
|
230
|
+
if q is None:
|
|
231
|
+
continue
|
|
232
|
+
if N.isnan(q.w):
|
|
233
|
+
continue
|
|
234
|
+
yield plate_id, q
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def get_rotation_series(conn, model, *times, **kwargs):
|
|
238
|
+
# Pre-compute plates for speed...
|
|
239
|
+
# LOL this actually makes things slower
|
|
240
|
+
check_model_id(conn, model)
|
|
241
|
+
|
|
242
|
+
plate_ages = conn.execute(
|
|
243
|
+
__plate_time_ranges,
|
|
244
|
+
dict(model_name=model),
|
|
245
|
+
).fetchall()
|
|
246
|
+
|
|
247
|
+
rowset = conn.execute(
|
|
248
|
+
__model_rotation_pairs,
|
|
249
|
+
dict(
|
|
250
|
+
model_name=model,
|
|
251
|
+
early_age=float(max(times)),
|
|
252
|
+
late_age=float(min(times)),
|
|
253
|
+
),
|
|
254
|
+
).fetchall()
|
|
255
|
+
for t in times:
|
|
256
|
+
t = float(t)
|
|
257
|
+
plates = [p.id for p in plate_ages if p.old_lim > t and p.young_lim < t]
|
|
258
|
+
kwargs["plates"] = plates
|
|
259
|
+
r = get_all_rotations(conn, model, t, safe=False, rowset=rowset, **kwargs)
|
|
260
|
+
yield dict(rotations=list(r), time=t)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_plate_rotations(conn, model, plate_id, verbose=False):
|
|
264
|
+
time_steps = range(0, 500, 1)
|
|
265
|
+
return ((get_rotation(conn, model, plate_id, t), t) for t in time_steps)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS corelle.model (
|
|
2
|
+
id serial PRIMARY KEY,
|
|
3
|
+
name text UNIQUE NOT NULL,
|
|
4
|
+
min_age numeric DEFAULT 0,
|
|
5
|
+
max_age numeric
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
CREATE TABLE IF NOT EXISTS corelle.plate (
|
|
9
|
+
id integer,
|
|
10
|
+
model_id integer NOT NULL REFERENCES corelle.model(id),
|
|
11
|
+
parent_id integer,
|
|
12
|
+
name text,
|
|
13
|
+
cotid text,
|
|
14
|
+
coid text,
|
|
15
|
+
PRIMARY KEY (id, model_id),
|
|
16
|
+
FOREIGN KEY (parent_id, model_id)
|
|
17
|
+
REFERENCES corelle.plate (id, model_id)
|
|
18
|
+
ON DELETE CASCADE
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS corelle.plate_polygon (
|
|
22
|
+
id serial PRIMARY KEY,
|
|
23
|
+
plate_id integer NOT NULL,
|
|
24
|
+
model_id integer NOT NULL,
|
|
25
|
+
young_lim numeric,
|
|
26
|
+
old_lim numeric,
|
|
27
|
+
geometry geometry(MultiPolygon, 4326),
|
|
28
|
+
FOREIGN KEY (plate_id, model_id)
|
|
29
|
+
REFERENCES corelle.plate (id, model_id)
|
|
30
|
+
ON DELETE CASCADE
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS
|
|
34
|
+
corelle_plate_polygon_model_id_idx
|
|
35
|
+
ON corelle.plate_polygon (plate_id, model_id);
|
|
36
|
+
|
|
37
|
+
CREATE INDEX
|
|
38
|
+
IF NOT EXISTS corelle_plate_polygon_geometry_idx
|
|
39
|
+
ON corelle.plate_polygon
|
|
40
|
+
USING gist (geometry);
|
|
41
|
+
|
|
42
|
+
CREATE TABLE IF NOT EXISTS corelle.rotation (
|
|
43
|
+
id serial PRIMARY KEY,
|
|
44
|
+
plate_id integer NOT NULL,
|
|
45
|
+
model_id integer NOT NULL,
|
|
46
|
+
t_step numeric NOT NULL,
|
|
47
|
+
latitude numeric NOT NULL,
|
|
48
|
+
longitude numeric NOT NULL,
|
|
49
|
+
angle numeric NOT NULL,
|
|
50
|
+
ref_plate_id integer NOT NULL,
|
|
51
|
+
metadata text,
|
|
52
|
+
UNIQUE (plate_id, model_id, t_step, ref_plate_id),
|
|
53
|
+
FOREIGN KEY (plate_id, model_id)
|
|
54
|
+
REFERENCES corelle.plate (id, model_id)
|
|
55
|
+
ON DELETE CASCADE,
|
|
56
|
+
FOREIGN KEY (ref_plate_id, model_id)
|
|
57
|
+
REFERENCES corelle.plate (id, model_id)
|
|
58
|
+
ON DELETE CASCADE
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
-- Features that can be clipped by plate IDs and returned
|
|
62
|
+
CREATE TABLE IF NOT EXISTS corelle.feature (
|
|
63
|
+
id serial PRIMARY KEY,
|
|
64
|
+
dataset_id text NOT NULL,
|
|
65
|
+
geometry geometry(Geometry, 4326),
|
|
66
|
+
properties jsonb
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
/* Cache of features pre-converted to GeoJSON (probably should be deprecated!)*/
|
|
70
|
+
CREATE TABLE IF NOT EXISTS corelle.feature_cache (
|
|
71
|
+
model_id integer NOT NULL REFERENCES corelle.model(id),
|
|
72
|
+
model_name text NOT NULL REFERENCES corelle.model(name),
|
|
73
|
+
dataset_id text NOT NULL,
|
|
74
|
+
geojson json NOT NULL,
|
|
75
|
+
PRIMARY KEY (model_id, dataset_id)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
/* Cache of rotations at various time steps */
|
|
79
|
+
CREATE TABLE IF NOT EXISTS corelle.rotation_cache (
|
|
80
|
+
model_id integer NOT NULL REFERENCES corelle.model(id),
|
|
81
|
+
plate_id integer NOT NULL,
|
|
82
|
+
t_step decimal NOT NULL,
|
|
83
|
+
rotation numeric[],
|
|
84
|
+
PRIMARY KEY (model_id, plate_id, t_step),
|
|
85
|
+
FOREIGN KEY (plate_id, model_id)
|
|
86
|
+
REFERENCES corelle.plate (id, model_id)
|
|
87
|
+
ON DELETE CASCADE
|
|
88
|
+
);
|