view pkg/imports/wx.go @ 1679:2dc7768be0e4

Waterway axis import: More on reading data from WFS. TODO: Parse to concrete features.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 26 Dec 2018 21:01:29 +0100
parents 53304db85888
children de8089944b19
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package imports

import (
	"context"
	"database/sql"
	"fmt"
	"io"
	"strings"
	"time"

	"gemma.intevation.de/gemma/pkg/common"
	"gemma.intevation.de/gemma/pkg/wfs"
)

type WaterwayAxis struct {
	URL         string `json:"url"`
	FeatureType string `json:"feature-type"`
	SortBy      string `json:"sort-by"`
}

const WXJobKind JobKind = "wx"

type wxJobCreator struct{}

func init() {
	RegisterJobCreator(WXJobKind, wxJobCreator{})
}

func (wxJobCreator) Description() string {
	return "waterway axis"
}

func (wxJobCreator) Create(_ JobKind, data string) (Job, error) {
	wx := new(WaterwayAxis)
	if err := common.FromJSONString(data, wx); err != nil {
		return nil, err
	}
	return wx, nil
}

func (wxJobCreator) Depends() []string {
	return []string{
		"waterway_axis",
	}
}

// StageDone is a NOP for waterway axis imports.
func (wxJobCreator) StageDone(context.Context, *sql.Tx, int64) error {
	return nil
}

// CleanUp for waterway imports is a NOP.
func (*WaterwayAxis) CleanUp() error { return nil }

// Do executes the actual waterway exis import.
func (wx *WaterwayAxis) Do(
	ctx context.Context,
	importID int64,
	conn *sql.Conn,
	feedback Feedback,
) (interface{}, error) {

	start := time.Now()

	feedback.Info("Import waterway axis")

	feedback.Info("Loading capabilities from %s", wx.URL)
	caps, err := wfs.GetCapabilities(wx.URL)
	if err != nil {
		feedback.Error("Loading capabilities failed: %v", err)
		return nil, err
	}

	ft := caps.FindFeatureType(wx.FeatureType)
	if ft == nil {
		err := fmt.Errorf("Unknown feature type '%s'", wx.FeatureType)
		feedback.Error("%v", err)
		return nil, err
	}

	urls, err := wfs.GetFeaturesGET(
		caps, wx.FeatureType, "application/json", wx.SortBy)
	if err != nil {
		feedback.Error("Cannot create GetFeature URLs. %v", err)
		return nil, err
	}

	var crsName string

	unsupportedTypes := map[string]int{}

	if err := wfs.DownloadURLs(urls, func(r io.Reader) error {
		rfc, err := wfs.ParseRawFeatureCollection(r)
		if err != nil {
			return err
		}
		if crsName != "" && rfc.CRS != nil {
			crsName = rfc.CRS.Properties.Name
		}

		// No features -> ignore.
		if rfc.Features == nil {
			return nil
		}
		for _, feature := range rfc.Features {
			switch feature.Geometry.Type {
			case "LineString":
				// TODO: Parse concrete features.
			case "MultiLineString":
				// TODO: Parse concrete features.
			default:
				unsupportedTypes[feature.Geometry.Type]++
			}
		}
		return nil
	}); err != nil {
		feedback.Error("Downloading features failed: %v", err)
		return nil, err
	}

	if len(unsupportedTypes) != 0 {
		var b strings.Builder
		for t, c := range unsupportedTypes {
			if b.Len() > 0 {
				b.WriteString(", ")
			}
			b.WriteString(fmt.Sprintf("%s: %d", t, c))
		}
		feedback.Warn("Unsupported types found: %s", b.String())
	}

	if crsName == "" {
		crsName = ft.DefaultCRS
	}

	epsg, err := wfs.CRSToEPSG(crsName)
	if err != nil {
		feedback.Error("Unsupported CRS name '%s'", crsName)
		return nil, err
	}

	feedback.Info("using ESPG: %d", epsg)

	// TODO: Store extracted features.

	feedback.Info("Storing took %s", time.Since(start))

	return nil, nil
}