fix: Fix vector with KATO geometry

This commit is contained in:
Bakhtiyar Issakhmetov 2026-06-28 18:48:24 +05:00
parent 4fdd12e9a3
commit f364ce4c6e
3 changed files with 34 additions and 11 deletions

View File

@ -166,11 +166,22 @@ func (r *DatasetRepository) SetProperties(ctx context.Context, id uuid.UUID, pro
return nil
}
// MarkReady sets the dataset status to ready and clears any error.
func (r *DatasetRepository) MarkReady(ctx context.Context, id uuid.UUID) error {
// MarkReady sets the dataset status to ready, stores the dissolved feature
// geometry (GeoJSON in EPSG:4326; nil keeps the existing geometry, reduced to
// the union of all features via ST_UnaryUnion), and clears any error.
func (r *DatasetRepository) MarkReady(ctx context.Context, id uuid.UUID, geometry []byte) error {
var geom any // nil -> SQL NULL -> CASE keeps existing geometry
if len(geometry) > 0 {
geom = string(geometry)
}
tag, err := r.pool.Exec(ctx,
`UPDATE datasets SET status = $2, parse_error = NULL, updated_at = now() WHERE id = $1`,
id, domain.DatasetStatusReady,
`UPDATE datasets
SET status = $2,
geometry = CASE WHEN $3::text IS NULL THEN geometry
ELSE ST_UnaryUnion(ST_SetSRID(ST_GeomFromGeoJSON($3), 4326)) END,
parse_error = NULL, updated_at = now()
WHERE id = $1`,
id, domain.DatasetStatusReady, geom,
)
if err != nil {
return mapError(err)

View File

@ -31,7 +31,7 @@ type DatasetRepository interface {
Delete(ctx context.Context, id uuid.UUID) error
MarkParsed(ctx context.Context, id uuid.UUID, cols []domain.AttributeColumn) error
MarkParseFailed(ctx context.Context, id uuid.UUID, reason string) error
MarkReady(ctx context.Context, id uuid.UUID) error
MarkReady(ctx context.Context, id uuid.UUID, geometry []byte) error
MarkConverted(ctx context.Context, id uuid.UUID, cogKey string, footprint []byte) error
SetProperties(ctx context.Context, id uuid.UUID, properties, geometry []byte) error
SaveMapping(ctx context.Context, id uuid.UUID, katoColumn string, years []domain.YearColumn) (domain.Dataset, error)
@ -515,9 +515,11 @@ func (s *DatasetService) SaveMapping(ctx context.Context, id uuid.UUID, in Mappi
}
// Extract reads a mapped dataset's file, unpivots its attribute table into
// observations keyed by KATO code and date, and marks the dataset ready. It is
// invoked by the worker. Permanent failures (unparsable file) are recorded;
// transient failures (storage/DB) are returned for retry.
// observations keyed by KATO code and date, records the dissolved feature
// geometry, and marks the dataset ready. It is invoked by the worker. Permanent
// failures (unparsable file) are recorded; transient failures (storage/DB) are
// returned for retry. Geometry extraction is best-effort: a failure leaves
// geometry unset rather than failing the job.
func (s *DatasetService) Extract(ctx context.Context, id uuid.UUID) error {
dataset, err := s.repo.GetByID(ctx, id)
if err != nil {
@ -541,7 +543,9 @@ func (s *DatasetService) Extract(ctx context.Context, id uuid.UUID) error {
if err := s.repo.ReplaceObservations(ctx, id, obs); err != nil {
return err // transient
}
return s.repo.MarkReady(ctx, id)
geometry := s.vectorGeometry(ctx, dataset.Filename, data)
return s.repo.MarkReady(ctx, id, geometry)
}
// buildObservations unpivots rows into observations. Rows without a KATO code

View File

@ -101,11 +101,14 @@ func (r *stubDatasetRepo) SaveMapping(_ context.Context, id uuid.UUID, kato stri
return d, nil
}
func (r *stubDatasetRepo) MarkReady(_ context.Context, id uuid.UUID) error {
func (r *stubDatasetRepo) MarkReady(_ context.Context, id uuid.UUID, geometry []byte) error {
d, ok := r.store[id]
if !ok {
return domain.ErrNotFound
}
if len(geometry) > 0 {
d.Geometry = geometry
}
d.Status = domain.DatasetStatusReady
r.store[id] = d
return nil
@ -765,7 +768,9 @@ func TestDatasetService_Extract(t *testing.T) {
}
rows := []map[string]string{{"като": "751010000", "F_2023": "100"}}
rp := RowParser(func(string, []byte) ([]map[string]string, error) { return rows, nil })
svc := NewDatasetService(repo, &stubStore{}, stubCategoryReader{exists: true}, &stubEnqueuer{}, noopParser, rp, &stubConverter{})
geom := []byte(`{"type":"GeometryCollection","geometries":[]}`)
conv := &stubConverter{vectorGeom: geom}
svc := NewDatasetService(repo, &stubStore{}, stubCategoryReader{exists: true}, &stubEnqueuer{}, noopParser, rp, conv)
if err := svc.Extract(context.Background(), id); err != nil {
t.Fatalf("unexpected error: %v", err)
@ -773,6 +778,9 @@ func TestDatasetService_Extract(t *testing.T) {
if repo.store[id].Status != domain.DatasetStatusReady {
t.Fatalf("want ready, got %q", repo.store[id].Status)
}
if string(repo.store[id].Geometry) != string(geom) {
t.Fatalf("want geometry %s, got %s", geom, repo.store[id].Geometry)
}
got := repo.observations[id]
if len(got) != 1 || got[0].KatoCode != "751010000" || got[0].Value == nil || *got[0].Value != 100 {
t.Fatalf("unexpected observations: %+v", got)