Skip to content

Commit

Permalink
Polishing
Browse files Browse the repository at this point in the history
  • Loading branch information
rkettelerij committed Oct 12, 2023
1 parent 328063a commit 9fbcc31
Showing 1 changed file with 48 additions and 77 deletions.
125 changes: 48 additions & 77 deletions ogc/features/datasources/geopackage/geopackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,55 +37,54 @@ type geoPackageBackend interface {
close()
}

type GpkgContent struct {
type GpkgFeatureTable struct {
TableName string `db:"table_name"`
DataType string `db:"data_type"`
Identifier string `db:"identifier"`
Description string `db:"description"`
GeometryColumnName string `db:"column_name"`
GeometryType string `db:"geometry_type_name"`
LastChange time.Time `db:"last_change"`
MinX float64 `db:"min_x"` // bbox
MinY float64 `db:"min_y"` // bbox
MaxX float64 `db:"max_x"` // bbox
MaxY float64 `db:"max_y"` // bbox
SrsId int64 `db:"srs_id"` //nolint:revive
MinX float64 `db:"min_x"` // bbox
MinY float64 `db:"min_y"` // bbox
MaxX float64 `db:"max_x"` // bbox
MaxY float64 `db:"max_y"` // bbox
SRS int64 `db:"srs_id"`

Columns *[]GpkgColumn
}

type GpkgColumn struct {
Cid int `db:"cid"`
Name string `db:"name"`
DataType string `db:"type"`
NotNull int `db:"notnull"`
DefaultValue int `db:"dflt_value"`
PrimaryKey int `db:"pk"`
Name string `db:"name"`
DataType string `db:"type"`
PrimaryKey int `db:"pk"`
}

type GeoPackage struct {
backend geoPackageBackend
gpkgContentByID map[string]*GpkgContent
backend geoPackageBackend
gpkgFeatureTableByID map[string]*GpkgFeatureTable
}

func NewGeoPackage(e *engine.Engine) *GeoPackage {
gpkgConfig := e.Config.OgcAPI.Features.Datasource.GeoPackage

gpkg := &GeoPackage{}
if gpkgConfig.Local != nil {
gpkg.backend = newLocalGeoPackage(gpkgConfig.Local)
} else if gpkgConfig.Cloud != nil {
gpkg.backend = newCloudBackedGeoPackage(gpkgConfig.Cloud)
} else {
geopackage := &GeoPackage{}
switch {
case gpkgConfig.Local != nil:
geopackage.backend = newLocalGeoPackage(gpkgConfig.Local)
case gpkgConfig.Cloud != nil:
geopackage.backend = newCloudBackedGeoPackage(gpkgConfig.Cloud)
default:
log.Fatal("unknown geopackage config encountered")
}

content, err := readGpkgContents(gpkg.backend.getDB())
featureTables, err := readGpkgContents(geopackage.backend.getDB())
if err != nil {
log.Fatal(err)
}
gpkg.gpkgContentByID = content
return gpkg
geopackage.gpkgFeatureTableByID = featureTables

return geopackage
}

func (g *GeoPackage) Close() {
Expand All @@ -95,13 +94,13 @@ func (g *GeoPackage) Close() {
func (g *GeoPackage) GetFeatures(collection string, cursor int64, limit int) (*domain.FeatureCollection, domain.Cursor, error) {
result := domain.FeatureCollection{}

gpkgContent, ok := g.gpkgContentByID[collection]
gpkgContent, ok := g.gpkgFeatureTableByID[collection]
if !ok {
return nil, domain.Cursor{}, fmt.Errorf("can't query collection '%s' since it doesn't exist in "+
"geopackage, available in geopackage: %v\n", collection, engine.Keys(g.gpkgContentByID))
"geopackage, available in geopackage: %v\n", collection, engine.Keys(g.gpkgFeatureTableByID))
}

log.Print(gpkgContent)
log.Print(gpkgContent) // TODO
query := "select * from addresses f where f.fid > 10 order by f.fid limit 3"

rows, err := g.backend.getDB().Queryx(query)
Expand All @@ -110,21 +109,30 @@ func (g *GeoPackage) GetFeatures(collection string, cursor int64, limit int) (*d
}
defer rows.Close()

cols, err := rows.Columns()
result.Features, err = g.toFeatures(rows)
if err != nil {
return nil, domain.Cursor{}, err
}

result.Features = make([]*domain.Feature, 0)
return &result, domain.Cursor{}, nil
}

func (g *GeoPackage) toFeatures(rows *sqlx.Rows) ([]*domain.Feature, error) {
result := make([]*domain.Feature, 0)
cols, err := rows.Columns()
if err != nil {
return result, err
}

for rows.Next() {
vals := make([]interface{}, len(cols))
valPtrs := make([]interface{}, len(cols))
for i := 0; i < len(cols); i++ {
valPtrs[i] = &vals[i]
}

if err = rows.Scan(valPtrs...); err != nil {
return nil, domain.Cursor{}, err
if err := rows.Scan(valPtrs...); err != nil {
return nil, err
}

feature := &domain.Feature{Feature: geojson.Feature{Properties: make(map[string]interface{})}}
Expand All @@ -141,16 +149,13 @@ func (g *GeoPackage) GetFeatures(collection string, cursor int64, limit int) (*d
case "geom":
geomData, ok := vals[i].([]byte)
if !ok {
return nil, domain.Cursor{}, err
return result, fmt.Errorf("TODO")
}
log.Println(geomData) // TODO

geo, err := gpkg.DecodeGeometry(geomData)
if err != nil {
return nil, domain.Cursor{}, err
return result, fmt.Errorf("TODO")
}
feature.Geometry = geojson.Geometry{Geometry: geo.Geometry}
//feature.Geometry.Geometry = geom.Point{0, 0} // TODO

case "minx", "miny", "maxx", "maxy", "min_zoom", "max_zoom":
// Skip these columns used for bounding box and zoom filtering
Expand All @@ -161,9 +166,7 @@ func (g *GeoPackage) GetFeatures(collection string, cursor int64, limit int) (*d
switch v := vals[i].(type) {
case []uint8:
asBytes := make([]byte, len(v))
for j := 0; j < len(v); j++ {
asBytes[j] = v[j]
}
copy(asBytes, v)
feature.Properties[colName] = string(asBytes)
case int64:
feature.Properties[colName] = v
Expand All @@ -176,14 +179,13 @@ func (g *GeoPackage) GetFeatures(collection string, cursor int64, limit int) (*d
case bool:
feature.Properties[colName] = v
default:
log.Printf("unexpected type for sqlite column data: %v: %T", cols[i], v)
return result, fmt.Errorf("unexpected type for sqlite column data: %v: %T", cols[i], v)
}
}
}
result.Features = append(result.Features, feature)
result = append(result, feature)
}

return &result, domain.Cursor{}, nil
return result, nil
}

func (g *GeoPackage) GetFeature(collection string, featureID string) (*domain.Feature, error) {
Expand All @@ -192,23 +194,23 @@ func (g *GeoPackage) GetFeature(collection string, featureID string) (*domain.Fe
return nil, nil
}

func readGpkgContents(db *sqlx.DB) (map[string]*GpkgContent, error) {
func readGpkgContents(db *sqlx.DB) (map[string]*GpkgFeatureTable, error) {
rows, err := db.Queryx(queryGpkgContent)
if err != nil {
return nil, fmt.Errorf("failed to retrieve gpkg_contents using query: %v\n, error: %w", queryGpkgContent, err)
}
defer rows.Close()

result := make(map[string]*GpkgContent, 10)
result := make(map[string]*GpkgFeatureTable, 10)
for rows.Next() {
row := GpkgContent{}
row := GpkgFeatureTable{}
if err = rows.StructScan(&row); err != nil {
return nil, fmt.Errorf("failed to read gpkg_contents record, error: %w", err)
}

// read metadata of table mentioned in gpkg_contents record
var columns []GpkgColumn
if err = db.Select(&columns, `select * from pragma_table_info("?");`, row.TableName); err != nil {
if err = db.Select(&columns, `select name, type, pk from pragma_table_info("?");`, row.TableName); err != nil {
return nil, fmt.Errorf("failed to read columns of table %s, error: %w", row.TableName, err)
}
row.Columns = &columns
Expand All @@ -225,34 +227,3 @@ func readGpkgContents(db *sqlx.DB) (map[string]*GpkgContent, error) {

return result, nil
}

// copied,tweaked from https://github.com/go-spatial/jivan
func ConvertFeatureID(v interface{}) (interface{}, error) {
switch aval := v.(type) {
case float64:
return uint64(aval), nil
case int64:
return uint64(aval), nil
case uint64:
return aval, nil
case uint:
return uint64(aval), nil
case int8:
return uint64(aval), nil
case uint8:
return uint64(aval), nil
case uint16:
return uint64(aval), nil
case int32:
return uint64(aval), nil
case uint32:
return uint64(aval), nil
case []byte:
return string(aval), nil
case string:
return aval, nil

default:
return 0, fmt.Errorf("cannot convert ID : %v", aval)
}
}

0 comments on commit 9fbcc31

Please sign in to comment.