Mercurial > gemma
comparison pkg/imports/sr.go @ 2529:45d51a49f191
SR import: Use own triangulation and clipping when importing sounding results.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Wed, 06 Mar 2019 17:51:58 +0100 |
parents | 1ec4c5633eb6 |
children | 3f61b84ae7a6 |
comparison
equal
deleted
inserted
replaced
2528:113912129481 | 2529:45d51a49f191 |
---|---|
102 WHERE id = ( | 102 WHERE id = ( |
103 SELECT key from import.track_imports | 103 SELECT key from import.track_imports |
104 WHERE import_id = $1 AND | 104 WHERE import_id = $1 AND |
105 relation = 'waterway.sounding_results'::regclass)` | 105 relation = 'waterway.sounding_results'::regclass)` |
106 | 106 |
107 insertPointsSQL = ` | 107 insertHullSQL = ` |
108 INSERT INTO waterway.sounding_results ( | 108 INSERT INTO waterway.sounding_results ( |
109 bottleneck_id, | 109 bottleneck_id, |
110 date_info, | 110 date_info, |
111 depth_reference, | 111 depth_reference, |
112 point_cloud, | |
113 area | 112 area |
114 ) VALUES ( | 113 ) SELECT |
115 (SELECT id from waterway.bottlenecks where objnam = $1), | 114 (SELECT id from waterway.bottlenecks where objnam = $1), |
116 $2::date, | 115 $2::date, |
117 $3, | 116 $3, |
118 ST_Transform(ST_GeomFromWKB($4, $6::integer), 4326)::geography, | 117 (SELECT |
119 (SELECT CASE WHEN $5::bytea IS NULL THEN | 118 CASE WHEN $5::bytea IS NULL THEN |
120 ST_Transform( | 119 ST_Transform(ST_ConcaveHull(ST_Force2D(ST_GeomFromWKB($4, $6::integer)), 0.7), 4326)::geography |
121 ST_ConcaveHull( | 120 ELSE |
122 ST_Force2D(ST_GeomFromWKB($4, $6::integer)), 0.7), 4326)::geography | 121 ST_Transform(ST_GeomFromWKB($5, $6::integer), 4326)::geography |
123 ELSE | 122 END) |
124 ST_Transform(ST_GeomFromWKB($5, $6::integer), 4326)::geography | |
125 END) | |
126 ) | |
127 RETURNING | 123 RETURNING |
128 id, | 124 id, |
129 ST_X(ST_Centroid(point_cloud::geometry)), | 125 ST_X(ST_Centroid(area::geometry)), |
130 ST_Y(ST_Centroid(point_cloud::geometry)), | 126 ST_Y(ST_Centroid(area::geometry)), |
131 CASE WHEN ST_Y(ST_Centroid(point_cloud::geometry)) > 0 THEN | 127 best_utm(area::geometry), |
132 32600 | 128 ST_AsBinary(ST_Transform(area::geometry, best_utm(area::geometry))) |
133 ELSE | 129 ` |
134 32700 | 130 |
135 END + floor((ST_X(ST_Centroid(point_cloud::geometry))+180)/6)::int + 1` | 131 reprojectPointsSQL = ` |
136 | 132 SELECT ST_AsBinary(ST_Transform(ST_GeomFromWKB($1, $2::integer), $3::integer)) |
133 ` | |
137 insertOctreeSQL = ` | 134 insertOctreeSQL = ` |
138 UPDATE waterway.sounding_results SET | 135 UPDATE waterway.sounding_results SET |
139 octree_checksum = $2, octree_index = $3 | 136 octree_checksum = $2, octree_index = $3 |
140 WHERE id = $1` | 137 WHERE id = $1` |
141 | 138 |
172 importID int64, | 169 importID int64, |
173 conn *sql.Conn, | 170 conn *sql.Conn, |
174 feedback Feedback, | 171 feedback Feedback, |
175 ) (interface{}, error) { | 172 ) (interface{}, error) { |
176 | 173 |
174 begin := time.Now() | |
175 | |
177 z, err := zip.OpenReader(filepath.Join(sr.Dir, "sr.zip")) | 176 z, err := zip.OpenReader(filepath.Join(sr.Dir, "sr.zip")) |
178 if err != nil { | 177 if err != nil { |
179 return nil, err | 178 return nil, err |
180 } | 179 } |
181 defer z.Close() | 180 defer z.Close() |
232 epsg uint32 | 231 epsg uint32 |
233 lat, lon float64 | 232 lat, lon float64 |
234 ) | 233 ) |
235 start := time.Now() | 234 start := time.Now() |
236 | 235 |
237 err = tx.QueryRow(insertPointsSQL, | 236 var hull []byte |
237 | |
238 xyzWKB := xyz.AsWKB() | |
239 | |
240 err = tx.QueryRow(insertHullSQL, | |
238 m.Bottleneck, | 241 m.Bottleneck, |
239 m.Date.Time, | 242 m.Date.Time, |
240 m.DepthReference, | 243 m.DepthReference, |
241 xyz.AsWKB(), | 244 xyzWKB, |
242 polygon.asWKB(), | 245 polygon.asWKB(), |
243 m.EPSG, | 246 m.EPSG, |
244 ).Scan(&id, &lat, &lon, &epsg) | 247 ).Scan( |
248 &id, | |
249 &lat, | |
250 &lon, | |
251 &epsg, | |
252 &hull, | |
253 ) | |
245 xyz, polygon = nil, nil // not need from now on. | 254 xyz, polygon = nil, nil // not need from now on. |
246 feedback.Info("storing points took %s", time.Since(start)) | 255 feedback.Info("Calculating hull took %s.", time.Since(start)) |
247 if err != nil { | 256 if err != nil { |
248 return nil, err | 257 return nil, err |
249 } | 258 } |
250 | |
251 feedback.Info("Best suited UTM EPSG: %d", epsg) | 259 feedback.Info("Best suited UTM EPSG: %d", epsg) |
252 | 260 |
253 feedback.Info("Triangulate...") | 261 start = time.Now() |
254 start = time.Now() | 262 |
255 tin, err := octree.GenerateTinByID(ctx, conn, id, epsg) | 263 var clippingPolygon octree.Polygon |
256 feedback.Info("triangulation took %s", time.Since(start)) | 264 if err := clippingPolygon.FromWKB(hull); err != nil { |
257 if err != nil { | 265 return nil, err |
258 return nil, err | 266 } |
259 } | 267 clippingPolygon.Indexify() |
260 | 268 |
261 if tin == nil { | 269 feedback.Info("Building clipping polygon took %v.", time.Since(start)) |
262 return nil, errors.New("cannot load TIN from database") | 270 |
263 } | 271 start = time.Now() |
264 | 272 |
265 feedback.Info("Building octree...") | 273 var reproj []byte |
274 | |
275 err = tx.QueryRow(reprojectPointsSQL, | |
276 xyzWKB, | |
277 m.EPSG, | |
278 epsg, | |
279 ).Scan(&reproj) | |
280 | |
281 if err != nil { | |
282 return nil, err | |
283 } | |
284 | |
285 if err := xyz.FromWKB(reproj); err != nil { | |
286 return nil, err | |
287 } | |
288 | |
289 feedback.Info("Reprojecting points took %v.", time.Since(start)) | |
290 feedback.Info("Number of reprojected points: %d", len(xyz)) | |
291 | |
292 start = time.Now() | |
293 | |
294 tri, err := octree.Triangulate(xyz) | |
295 if err != nil { | |
296 return nil, err | |
297 } | |
298 | |
299 feedback.Info("Triangulation took %v.", time.Since(start)) | |
300 | |
301 start = time.Now() | |
302 | |
303 tin := tri.Tin() | |
304 | |
305 var str octree.STRTree | |
306 str.Build(tin) | |
307 | |
308 feedback.Info("Building STR tree took %v", time.Since(start)) | |
309 | |
310 start = time.Now() | |
311 | |
312 removed := str.Clip(&clippingPolygon) | |
313 feedback.Info("Clipping STR tree took %v.", time.Since(start)) | |
314 feedback.Info("Number of triangles to clip %d.", len(removed)) | |
315 | |
316 start = time.Now() | |
317 | |
266 builder := octree.NewBuilder(tin) | 318 builder := octree.NewBuilder(tin) |
267 start = time.Now() | 319 builder.Build(removed) |
268 builder.Build(nil) | |
269 octreeIndex, err := builder.Bytes() | 320 octreeIndex, err := builder.Bytes() |
270 tin = nil // not needed from now on | 321 feedback.Info("Building octree took %v.", |
271 feedback.Info("building octree took %s", time.Since(start)) | 322 time.Since(start)) |
272 if err != nil { | 323 if err != nil { |
273 return nil, err | 324 return nil, err |
274 } | 325 } |
275 | 326 |
276 feedback.Info("Store octree...") | |
277 start = time.Now() | 327 start = time.Now() |
278 h := sha1.New() | 328 h := sha1.New() |
279 h.Write(octreeIndex) | 329 h.Write(octreeIndex) |
280 checksum := hex.EncodeToString(h.Sum(nil)) | 330 checksum := hex.EncodeToString(h.Sum(nil)) |
281 _, err = tx.Exec(insertOctreeSQL, id, checksum, octreeIndex) | 331 _, err = tx.Exec(insertOctreeSQL, id, checksum, octreeIndex) |
282 feedback.Info("storing octree index took %s", time.Since(start)) | 332 feedback.Info("Storing octree index took %s.", |
333 time.Since(start)) | |
283 if err != nil { | 334 if err != nil { |
284 return nil, err | 335 return nil, err |
285 } | 336 } |
286 | 337 |
287 tree := builder.Tree() | 338 tree := builder.Tree() |
339 tree.EPSG = epsg | |
288 builder = nil // not needed from now on | 340 builder = nil // not needed from now on |
289 | 341 |
290 feedback.Info("Generate contour lines...") | |
291 start = time.Now() | 342 start = time.Now() |
292 err = generateContours(tree, tx, id) | 343 err = generateContours(tree, tx, id) |
293 feedback.Info("generating and storing contour lines took %s", time.Since(start)) | 344 feedback.Info("Generating and storing contour lines took %s.", |
345 time.Since(start)) | |
294 if err != nil { | 346 if err != nil { |
295 return nil, err | 347 return nil, err |
296 } | 348 } |
297 | 349 |
298 // Store for potential later removal. | 350 // Store for potential later removal. |
299 if err = track(ctx, tx, importID, "waterway.sounding_results", id); err != nil { | 351 if err = track(ctx, tx, importID, "waterway.sounding_results", id); err != nil { |
300 return nil, err | 352 return nil, err |
301 } | 353 } |
302 | 354 |
303 if err = tx.Commit(); err == nil { | 355 if err = tx.Commit(); err != nil { |
304 feedback.Info("Storing sounding result was successful.") | 356 feedback.Error( |
305 } | 357 "Storing sounding result failed after %v.", |
358 time.Since(begin)) | |
359 return nil, err | |
360 } | |
361 | |
362 feedback.Info( | |
363 "Storing sounding result was successful after %v.", | |
364 time.Since(begin)) | |
306 | 365 |
307 summary := struct { | 366 summary := struct { |
308 Bottleneck string `json:"bottleneck"` | 367 Bottleneck string `json:"bottleneck"` |
309 Date models.Date `json:"date"` | 368 Date models.Date `json:"date"` |
310 Lat float64 `json:"lat"` | 369 Lat float64 `json:"lat"` |
471 for ; h <= tree.Max.Z; h += contourStepWidth { | 530 for ; h <= tree.Max.Z; h += contourStepWidth { |
472 heights = append(heights, h) | 531 heights = append(heights, h) |
473 } | 532 } |
474 | 533 |
475 octree.DoContours(tree, heights, func(res *octree.ContourResult) { | 534 octree.DoContours(tree, heights, func(res *octree.ContourResult) { |
476 if err == nil { | 535 if err == nil && len(res.Lines) > 0 { |
477 _, err = stmt.Exec( | 536 _, err = stmt.Exec( |
478 id, res.Height, tree.EPSG, | 537 id, res.Height, tree.EPSG, |
479 res.Lines.AsWKB2D(), | 538 res.Lines.AsWKB2D(), |
480 contourTolerance) | 539 contourTolerance) |
481 } | 540 } |