Skip to content

Commit 6926c01

Browse files
authored
feat(server): Allow customization of associated point (#99)
1 parent 01da471 commit 6926c01

File tree

6 files changed

+97
-25
lines changed

6 files changed

+97
-25
lines changed

internal/server/postgres/dataserverimpl.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,10 +1108,21 @@ func (s *DataPlatformDataServiceServerImpl) CreateLocation(
11081108

11091109
querier := db.New(ix.GetTxFromContext(ctx))
11101110

1111+
var associated_point *string
1112+
if req.AssociatedLatlng != nil {
1113+
point := fmt.Sprintf(
1114+
"POINT(%f %f)",
1115+
req.AssociatedLatlng.Longitude,
1116+
req.AssociatedLatlng.Latitude,
1117+
)
1118+
associated_point = &point
1119+
}
1120+
11111121
cgprms := db.CreateGeometryParams{
1112-
GeometryName: req.LocationName,
1113-
Geom: req.GeometryWkt,
1114-
GeometryTypeID: int16(req.LocationType),
1122+
GeometryName: req.LocationName,
1123+
Geom: req.GeometryWkt,
1124+
GeometryTypeID: int16(req.LocationType),
1125+
AssociatedPoint: associated_point,
11151126
}
11161127

11171128
dbLocation, err := querier.CreateGeometry(ctx, cgprms)

internal/server/postgres/dataserverimpl_test.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ func TestCreateLocation(t *testing.T) {
115115
require.NoError(t, err)
116116

117117
testcases := []struct {
118-
name string
119-
req *pb.CreateLocationRequest
118+
name string
119+
req *pb.CreateLocationRequest
120+
expectedLatLng *pb.LatLng
120121
}{
121122
{
122123
name: "Should create solar location",
@@ -128,6 +129,10 @@ func TestCreateLocation(t *testing.T) {
128129
LocationType: pb.LocationType_LOCATION_TYPE_SITE,
129130
Metadata: metadata,
130131
},
132+
expectedLatLng: &pb.LatLng{
133+
Latitude: 51.5,
134+
Longitude: 0.0,
135+
},
131136
},
132137
{
133138
name: "Shouldn't create unknown energy source",
@@ -150,6 +155,10 @@ func TestCreateLocation(t *testing.T) {
150155
LocationType: pb.LocationType_LOCATION_TYPE_SITE,
151156
Metadata: metadata,
152157
},
158+
expectedLatLng: &pb.LatLng{
159+
Latitude: 51.5,
160+
Longitude: 0.0,
161+
},
153162
},
154163
{
155164
name: "Shouldn't create unknown location type",
@@ -183,6 +192,10 @@ func TestCreateLocation(t *testing.T) {
183192
LocationType: pb.LocationType_LOCATION_TYPE_GSP,
184193
Metadata: metadata,
185194
},
195+
expectedLatLng: &pb.LatLng{
196+
Latitude: 51.75,
197+
Longitude: 0.5,
198+
},
186199
},
187200
{
188201
name: "Shouldn't create location with non-closed POLYGON geometry",
@@ -205,6 +218,10 @@ func TestCreateLocation(t *testing.T) {
205218
LocationType: pb.LocationType_LOCATION_TYPE_DNO,
206219
Metadata: metadata,
207220
},
221+
expectedLatLng: &pb.LatLng{
222+
Latitude: 51.75,
223+
Longitude: 1.5,
224+
},
208225
},
209226
{
210227
name: "Shouldn't create location with non-closed MULTIPOLYGON geometry",
@@ -228,6 +245,25 @@ func TestCreateLocation(t *testing.T) {
228245
Metadata: metadata,
229246
},
230247
},
248+
{
249+
name: "Should create location with associated lat long",
250+
req: &pb.CreateLocationRequest{
251+
LocationName: "closed_multipolygon_with_latlng",
252+
EnergySource: pb.EnergySource_ENERGY_SOURCE_WIND,
253+
GeometryWkt: "MULTIPOLYGON(((0.0 51.5, 1.0 51.5, 1.0 52.0, 0.0 52.0, 0.0 51.5)),((2.0 51.5, 3.0 51.5, 3.0 52.0, 2.0 52.0, 2.0 51.5)))",
254+
EffectiveCapacityWatts: 14e6,
255+
LocationType: pb.LocationType_LOCATION_TYPE_DNO,
256+
Metadata: metadata,
257+
AssociatedLatlng: &pb.LatLng{
258+
Latitude: 51.5074,
259+
Longitude: -0.1278,
260+
},
261+
},
262+
expectedLatLng: &pb.LatLng{
263+
Latitude: 51.5074,
264+
Longitude: -0.1278,
265+
},
266+
},
231267
}
232268

233269
for _, tc := range testcases {
@@ -253,6 +289,7 @@ func TestCreateLocation(t *testing.T) {
253289
// require.Equal(t, tc.req.GeometryWkt, string(resp2.GeometryWkb))
254290
require.Equal(t, tc.req.EffectiveCapacityWatts, resp2.EffectiveCapacityWatts)
255291
require.Equal(t, tc.req.Metadata.AsMap(), resp2.Metadata.AsMap())
292+
require.Equal(t, tc.expectedLatLng, resp2.Latlng)
256293
}
257294
})
258295
}

internal/server/postgres/sql/migrations/00002_locations.sql

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,14 @@ CREATE TABLE loc.geometries (
7676
REFERENCES loc.geometry_types (geometry_type_id)
7777
ON UPDATE CASCADE
7878
ON DELETE RESTRICT,
79-
centroid GEOMETRY (POINT, 4326) GENERATED ALWAYS AS (ST_CENTROID(geom)) STORED,
79+
associated_point GEOMETRY (POINT, 4326) NOT NULL,
80+
CONSTRAINT associated_point_validity_check CHECK (
81+
ST_SRID(associated_point) = 4326
82+
AND ST_NDIMS(associated_point) = 2
83+
AND ST_ISVALID(associated_point)
84+
AND ST_X(associated_point) >= -180 AND ST_X(associated_point) <= 180
85+
AND ST_Y(associated_point) >= -90 AND ST_Y(associated_point) <= 90
86+
),
8087
geom_hash TEXT GENERATED ALWAYS AS (MD5(ST_ASBINARY(geom))) STORED,
8188
metadata JSONB DEFAULT NULL,
8289
PRIMARY KEY (geometry_uuid),
@@ -139,8 +146,8 @@ SELECT
139146
sh.metadata,
140147
g.geometry_name,
141148
g.geometry_type_id,
142-
ST_X(g.centroid)::REAL AS longitude,
143-
ST_Y(g.centroid)::REAL AS latitude,
149+
ST_X(g.associated_point)::REAL AS longitude,
150+
ST_Y(g.associated_point)::REAL AS latitude,
144151
TSRANGE(
145152
sh.valid_from_utc,
146153
LEAD(sh.valid_from_utc, 1) OVER (

internal/server/postgres/sql/queries/locations.sql

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22

33
-- name: CreateGeometry :one
44
INSERT INTO loc.geometries AS l (
5-
geometry_name, geom, geometry_type_id
5+
geometry_name, geom, geometry_type_id, associated_point
66
) VALUES (
77
LOWER(sqlc.arg(geometry_name)::TEXT),
8-
ST_GEOMFROMTEXT(sqlc.arg(geom)::TEXT, 4326), --Ensure in WSG84
9-
$1
10-
) RETURNING l.geometry_uuid, l.geometry_name, ST_X(l.centroid)::REAL AS longitude, ST_Y(l.centroid)::REAL AS latitude;
8+
ST_GEOMFROMTEXT(sqlc.arg(geom)::TEXT, 4326),
9+
$1,
10+
COALESCE(
11+
ST_GEOMFROMTEXT(sqlc.narg(associated_point)::TEXT, 4326),
12+
ST_CENTROID(ST_GEOMFROMTEXT(sqlc.arg(geom)::TEXT, 4326))
13+
)
14+
) RETURNING
15+
l.geometry_uuid, l.geometry_name, ST_X(l.associated_point)::REAL AS longitude, ST_Y(l.associated_point)::REAL AS latitude;
1116

1217
-- name: RenameGeometry :one
1318
UPDATE loc.geometries AS l
1419
SET geometry_name = LOWER(sqlc.arg(new_geometry_name)::TEXT)
1520
WHERE l.geometry_uuid = $1
16-
RETURNING l.geometry_uuid, l.geometry_name, ST_X(l.centroid)::REAL AS longitude, ST_Y(l.centroid)::REAL AS latitude;
21+
RETURNING
22+
l.geometry_uuid, l.geometry_name, ST_X(l.associated_point)::REAL AS longitude, ST_Y(l.associated_point)::REAL AS latitude;
1723

1824
-- name: GetGeometryGeoJSON :one
1925
/* GetLocationGeoJSON returns a GeoJSON FeatureCollection for the given geometries.
@@ -52,8 +58,8 @@ SELECT
5258
s.geometry_uuid,
5359
l.geometry_name,
5460
s.sys_period,
55-
ST_X(l.centroid)::REAL AS longitude,
56-
ST_Y(l.centroid)::REAL AS latitude
61+
ST_X(l.associated_point)::REAL AS longitude,
62+
ST_Y(l.associated_point)::REAL AS latitude
5763
FROM loc.sources_mv AS s
5864
INNER JOIN loc.geometries AS l USING (geometry_uuid)
5965
WHERE
@@ -127,8 +133,8 @@ WITH unfiltered_sources AS (
127133
ls.capacity_limit_sip,
128134
l.geometry_name,
129135
l.geometry_type_id,
130-
ST_X(l.centroid)::REAL AS longitude,
131-
ST_Y(l.centroid)::REAL AS latitude,
136+
ST_X(l.associated_point)::REAL AS longitude,
137+
ST_Y(l.associated_point)::REAL AS latitude,
132138
l.metadata AS geometry_metadata,
133139
ls.metadata AS source_metadata
134140
FROM loc.sources_mv AS ls
@@ -161,7 +167,7 @@ WITH contained_geometries AS (
161167
l.geometry_name,
162168
l.geometry_type_id,
163169
l.geom,
164-
l.centroid,
170+
l.associated_point,
165171
l.metadata
166172
FROM loc.geometries AS l
167173
INNER JOIN
@@ -181,8 +187,8 @@ unfiltered_sources AS (
181187
ls.capacity_limit_sip,
182188
l.geometry_name,
183189
l.geometry_type_id,
184-
ST_X(l.centroid)::REAL AS longitude,
185-
ST_Y(l.centroid)::REAL AS latitude,
190+
ST_X(l.associated_point)::REAL AS longitude,
191+
ST_Y(l.associated_point)::REAL AS latitude,
186192
l.metadata AS geometry_metadata,
187193
ls.metadata AS source_metadata
188194
FROM loc.sources_mv AS ls
@@ -215,12 +221,12 @@ WITH containing_geometries AS (
215221
l.geometry_name,
216222
l.geometry_type_id,
217223
l.geom,
218-
l.centroid,
224+
l.associated_point,
219225
l.metadata
220226
FROM loc.geometries AS l
221227
INNER JOIN
222228
loc.geometries AS l_inner ON ST_WITHIN(
223-
l_inner.geom,
229+
l_inner.associated_point,
224230
l.geom
225231
) AND l_inner.geometry_uuid = sqlc.arg(inner_geometry_uuid)::UUID
226232
AND l.geometry_uuid <> sqlc.arg(inner_geometry_uuid)::UUID
@@ -235,8 +241,8 @@ unfiltered_sources AS (
235241
ls.capacity_limit_sip,
236242
l.geometry_name,
237243
l.geometry_type_id,
238-
ST_X(l.centroid)::REAL AS longitude,
239-
ST_Y(l.centroid)::REAL AS latitude,
244+
ST_X(l.associated_point)::REAL AS longitude,
245+
ST_Y(l.associated_point)::REAL AS latitude,
240246
l.metadata AS geometry_metadata,
241247
ls.metadata AS source_metadata
242248
FROM loc.sources_mv AS ls

internal/server/postgres/testdata/seeding.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ BEGIN
2222

2323
-- Insert geometries
2424
INSERT INTO loc.geometries
25-
(geometry_name, geometry_type_id, geom)
25+
(geometry_name, geometry_type_id, geom, associated_point)
2626
SELECT
2727
LOWER(name_prefix) || '_testgeometry' || i AS geometry_name,
2828
1,
29+
ST_SetSRID(ST_MakePoint(random() * 355 - 180, random() * 175 - 90), 4326),
2930
ST_SetSRID(ST_MakePoint(random() * 355 - 180, random() * 175 - 90), 4326)
3031
FROM generate_series(0, num_locations - 1) as i;
3132
RAISE NOTICE 'Inserted % geometries', (SELECT COUNT(*) FROM loc.geometries);

proto/ocf/dp/dp-data.messages.proto

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,16 @@ message CreateLocationRequest {
337337
optional google.protobuf.Timestamp valid_from_utc = 7 [
338338
(buf.validate.field).timestamp = { gt: { seconds: 112000000}, lt_now: true }
339339
];
340+
// Optional latitude/longitude to associate with the location.
341+
// Defaults to the centroid of the geometry if not provided. Not required for Point geometries.
342+
optional LatLng associated_latlng = 8;
343+
344+
option (buf.validate.message).cel = {
345+
id: "latlng_invalid_for_point"
346+
message: "latlng must not be provided for Point geometries"
347+
expression:
348+
"this.geometry_wkt.startsWith('POINT') ? !has(this.associated_latlng) : true"
349+
};
340350
}
341351

342352
message CreateLocationResponse {

0 commit comments

Comments
 (0)