view pkg/wfs/global.go @ 5591:0011f50cf216 surveysperbottleneckid

Removed no longer used alternative api for surveys/ endpoint. As bottlenecks in the summary for SR imports are now identified by their id and no longer by the (not guarantied to be unique!) name, there is no longer the need to request survey data by the name+date tuple (which isn't reliable anyway). So the workaround was now reversed.
author Sascha Wilde <wilde@sha-bang.de>
date Wed, 06 Apr 2022 13:30:29 +0200
parents 09346efa7f69
children 31973f6f5cca
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) 2019 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package wfs

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sync"

	"gemma.intevation.de/gemma/pkg/config"
	"gemma.intevation.de/gemma/pkg/log"
)

var (
	// FormatGeoJSON is a list of MIME types of
	// GeoJSON documents.
	FormatGeoJSON = []string{"geojson", "application/json"}
	// FormatGML is a list of MIME types
	// GML documents.
	FormatGML = []string{
		"application/gml+xml; version=3.2",
		"gml2", "gml3", "gml32",
		"text/xml; subtype=gml/2.1.2",
		"text/xml; subtype=gml/3.1.1",
		"text/xml; subtype=gml/3.2",
	}
)

type (
	// Downloader abstracts the idea of a download function for WFS.
	Downloader interface {
		Download(user, password string, handler func(string, io.Reader) error) error
	}

	// GeoJSONDownloader is a Downloader for GeoJSON.
	GeoJSONDownloader []string
	// GMLDownloader is a Downloader for GML.
	GMLDownloader []string
)

var (
	getFeaturesOnce sync.Once
	getFeatures     func(*Capabilities, string, string) (Downloader, error)
)

func getFeaturesOnceFunc() {
	path, err := exec.LookPath("ogr2ogr")
	if err != nil {
		log.Infoln("ogr2ogr not installed. Using direct GeoJSON WFS download.")
		getFeatures = getFeaturesGeoJSON
	} else {
		log.Infof("ogr2ogr found at %s. Using GML WFS download.\n", path)
		getFeatures = getFeaturesGML
	}
}

// GetFeatures is the default Downloader in this Gemma server.
func GetFeatures(
	caps *Capabilities,
	featureTypeName string,
	sortBy string,
) (Downloader, error) {
	getFeaturesOnce.Do(getFeaturesOnceFunc)
	return getFeatures(caps, featureTypeName, sortBy)
}

func getFeaturesGeoJSON(
	caps *Capabilities,
	featureTypeName string,
	sortBy string,
) (Downloader, error) {
	urls, err := GetFeaturesGET(
		caps,
		featureTypeName,
		FormatGeoJSON,
		sortBy)
	return GeoJSONDownloader(urls), err
}

func getFeaturesGML(
	caps *Capabilities,
	featureTypeName string,
	sortBy string,
) (Downloader, error) {
	urls, err := GetFeaturesGET(
		caps,
		featureTypeName,
		FormatGML,
		sortBy)
	return GMLDownloader(urls), err
}

// Download is the GeoJSON implementation of the Downloader.
func (gjd GeoJSONDownloader) Download(user, password string, handler func(string, io.Reader) error) error {
	return DownloadURLs(user, password, []string(gjd), handler)
}

func places(n int) int {
	places := 1
	if n < 0 {
		places++
		n = -n
	}
	for value := 10; n >= value; value *= 10 {
		places++
	}
	return places
}

// Download is GML implementaion of the Downloader.
func (gmd GMLDownloader) Download(user, password string, handler func(string, io.Reader) error) error {

	if len(gmd) == 0 {
		return nil
	}

	tmpDir := config.TmpDir()
	dlDir, err := ioutil.TempDir(tmpDir, "wfs-downloads")
	if err != nil {
		return err
	}
	defer os.RemoveAll(dlDir)
	digits := places(len(gmd))

	var files []string

	for i, url := range gmd {
		fname := filepath.Join(dlDir, fmt.Sprintf("%0*d.gml", digits, i))
		if err := downloadURL(user, password, url, func(_ string, r io.Reader) error {
			f, err := os.Create(fname)
			if err != nil {
				return err
			}
			if _, err = io.Copy(f, r); err != nil {
				f.Close()
				return err
			}
			return f.Close()
		}); err != nil {
			return err
		}
		files = append(files, fname)
	}

	out := filepath.Join(dlDir, "out.geojson")

	cmd := exec.Command("ogr2ogr", append([]string{"-f", "GeoJSON", out}, files...)...)

	msg, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("ogr2ogr failed: %s", msg)
	}

	in, err := os.Open(out)
	if err != nil {
		return err
	}
	defer in.Close()

	return handler(gmd[0], bufio.NewReader(in))
}