flatgeobuf

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2023 License: MIT Imports: 11 Imported by: 3

Documentation

Overview

Package flatgeobuf enables reading and writing FlatGeobuf files.

Index

Examples

Constants

View Source
const (
	// MinSpecMajorVersion is the minimum major version of the
	// FlatGeobuf specification that this package can read.
	MinSpecMajorVersion = 0x03
	// MaxSpecMajorVersion is the maximum major version of the
	// FlatGeobuf specification that this package can read.
	MaxSpecMajorVersion = 0x03
)

Variables

View Source
var (
	// ErrNoIndex is returned when attempting to do an index read or
	// search on a FlatGeobuf file that has no index.
	ErrNoIndex = textErr("no index")
	// ErrNotSeekable is returned from a FileReader's Rewind method if
	// its underlying stream does not implement io.Seeker.
	ErrNotSeekable = textErr("can't rewind: reader is not an io.Seeker")
	// ErrClosed is returned when attempting to perform an operation on
	// a FileReader or FileWriter which has been closed.
	ErrClosed = textErr("closed")
)

Functions

func FeatureString

func FeatureString(f *flat.Feature, s Schema) string

FeatureString returns a string summarizing the Feature. The returned value is a summary and not meant to be exhaustive.

Property column names are taken from the Feature's column schema, if it has one. If not, they are taken from the supplied Schema parameter if it is not nil. The supplied Schema parameter will typically be the *flat.Header from the feature's FlatGeobuf file.

func HeaderString

func HeaderString(hdr *flat.Header) string

HeaderString returns a string summarizing the Header fields. The returned value is a summary and not meant to be exhaustive.

Types

type FileReader

type FileReader struct {
	// contains filtered or unexported fields
}

FileReader reads an underlying io.Reader stream as a FlatGeobuf file.

The underlying stream may optionally implement io.Seeker to enable streaming index searches via IndexSearch and Rewind.

Example (EmptyFile)
package main

import (
	"bytes"
	"compress/bzip2"
	"encoding/base64"
	"fmt"
	"io"
	"strings"

	"github.com/gogama/flatgeobuf/flatgeobuf"

	testdata "github.com/gogama/flatgeobuf/flatgeobuf/testdata/flatgeobuf"
)

func testdataReader(data string) io.Reader {
	var r io.Reader
	r = strings.NewReader(data)
	r = base64.NewDecoder(base64.StdEncoding, r)
	r = bzip2.NewReader(r)
	b, err := io.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return bytes.NewReader(b)
}

func main() {
	// This simple example reads a trivial, empty, FlatGeobuf file. It
	// opens the file, reads the FlatGeobuf header, attempts to read the
	// index (but gets an error because the file has no index), and
	// reads the data section, which contains no features.

	r := flatgeobuf.NewFileReader(testdataReader(testdata.EmptyFGB))
	defer r.Close()

	hdr, err := r.Header()
	if err != nil {
		panic(err)
	}
	fmt.Println(flatgeobuf.HeaderString(hdr))

	index, err := r.Index()
	fmt.Printf("Index = %v, err = %v\n", index, err)

	data, err := r.DataRem()
	fmt.Printf("Data = %v, err = %v\n", data, err)
}
Output:

Header{Name:gps_mobile_tiles,Type:Polygon,Columns:6,Features:Unknown,No Index,CRS:{Org:EPSG,Code:4326,Name:WGS 84,WKT:821 bytes}}
Index = <nil>, err = flatgeobuf: no index
Data = [], err = <nil>
Example (UnknownFeatureCount)
package main

import (
	"bytes"
	"compress/bzip2"
	"encoding/base64"
	"fmt"
	"io"
	"strings"

	"github.com/gogama/flatgeobuf/flatgeobuf"

	testdata "github.com/gogama/flatgeobuf/flatgeobuf/testdata/flatgeobuf"
)

func testdataReader(data string) io.Reader {
	var r io.Reader
	r = strings.NewReader(data)
	r = base64.NewDecoder(base64.StdEncoding, r)
	r = bzip2.NewReader(r)
	b, err := io.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return bytes.NewReader(b)
}

func main() {
	// This example reads a FlatGeobuf file which has an unknown feature
	// count, indicated by a zero in the header's feature count field.
	// The FileReader's DataRem() method provides a one-liner read all
	// available features at once. It is equivalent to using Data() in
	// a loop until EOF is reached.

	r := flatgeobuf.NewFileReader(testdataReader(testdata.UnknownFeatureCountFGB))
	defer r.Close()

	hdr, err := r.Header()
	if err != nil {
		panic(err)
	}
	fmt.Println(flatgeobuf.HeaderString(hdr))

	data, _ := r.DataRem() // Ignoring error to simplify example only!
	if len(data) > 0 {
		fmt.Printf("len(Data) -> %d, Data[0] -> %s\n", len(data), flatgeobuf.FeatureString(&data[0], hdr))
	}
}
Output:

Header{Name:gps_mobile_tiles,Type:Polygon,Columns:6,Features:Unknown,No Index,CRS:{Org:EPSG,Code:4326,Name:WGS 84,WKT:821 bytes}}
len(Data) -> 1, Data[0] -> Feature{Geometry:{Type:Unknown,Bounds:[-69.911499,18.458768,-69.906006,18.463979]},Properties:{quadkey:0322113021201023,avg_d_kbps:16109,avg_u_kbps:11204,avg_lat_ms:36,tests:98,devices:49}}

func NewFileReader

func NewFileReader(r io.Reader) *FileReader

NewFileReader creates a new FlatGeobuf file reader based on an underlying input stream.

The underlying reader must be positioned at the beginning of the file, i.e. right before the FlatGeobuf magic number.

If the underlying reader implements the io.Seeker interface and the underlying FlatGeobuf file has an index, the index can be searched in a streaming manner using the new FileReader's IndexSearch method.

func (*FileReader) Close

func (r *FileReader) Close() error

Close closes the FileReader. All subsequent calls to any method will return ErrClosed.

If the underlying stream implements io.Closer, this method invokes Close on the underlying stream and returns the result.

func (*FileReader) Data

func (r *FileReader) Data(p []flat.Feature) (int, error)

Data reads up to len(p) feature structures from the FlatGeobuf data section into p. If fewer than len(p) features remain to be read then only the remaining features are read into p. The number of features (not bytes!) actually read is returned.

This method may only be called once Header has been called. If a previous call to Data has not been made since the last successful Header or Rewind call, Data starts reading from the beginning of the data section. Otherwise, it resumes reading from the position that the last Data call left off.

If no features remain to be read, the return value is a count of 0 and the error io.EOF. This method will never return io.EOF if the count returned is positive; but any other I/O error maybe returned with a positive count, for example io.ErrUnexpectedEOF.

func (*FileReader) DataRem

func (r *FileReader) DataRem() ([]flat.Feature, error)

DataRem reads and returns all remaining unread features from the FlatGeobuf data section.

This method may only be called once Header has been called. If a previous call to Data has not been made since the last successful Header or Rewind call, DataRem reads all features from the data section. Otherwise, it reads all features remaining after the last Data call left off.

func (*FileReader) Header

func (r *FileReader) Header() (*flat.Header, error)

Header reads and returns the FlatBuffer table containing the FlatGeobuf file's header table.

This method may only be called once, immediately after creating the FileReader via NewFileReader. Once the reader has advanced past the header, it cannot be read again.

If the header table cannot be read, the return value is a nil pointer and an error. If the header table was successfully read and contains usable values, the return value is a non-nil pointer and a nil error. Lastly, if the header table was successfully read, but the feature count or index node size values it contains are unusable, the return value is a non-nil pointer and an error, in which case this reader will transition to a permanent error state from which only the Close() method will work without further error.

func (*FileReader) Index

func (r *FileReader) Index() (*packedrtree.PackedRTree, error)

Index reads and returns an in-memory implementation of the FlatGeobuf file's index data structure. If the FlatGeobuf file has no index, the error ErrNoIndex is returned.

This method may only be called immediately after a successful call to Header or Rewind.

As an alternative to calling Index, consider IndexSearch, which combines reading the index data structure, searching it, and returning the features for each qualified match in the search results.

Example
package main

import (
	"bytes"
	"compress/bzip2"
	"encoding/base64"
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/gogama/flatgeobuf/flatgeobuf"
	"github.com/gogama/flatgeobuf/flatgeobuf/flat"

	testdata "github.com/gogama/flatgeobuf/flatgeobuf/testdata/flatgeobuf"
	"github.com/gogama/flatgeobuf/packedrtree"
)

func testdataReader(data string) io.Reader {
	var r io.Reader
	r = strings.NewReader(data)
	r = base64.NewDecoder(base64.StdEncoding, r)
	r = bzip2.NewReader(r)
	b, err := io.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return bytes.NewReader(b)
}

func main() {
	// This example reads from a FlatGeobuf file which contains an
	// index. It reads the entire index data structure into memory using
	// the FileReader's Index() method, searches the index to find
	// candidate features that may intersect a bounding box, then
	// reads the data section up to the first candidate feature and
	// prints a string summary of the candidate.

	r := flatgeobuf.NewFileReader(testdataReader(testdata.CountriesFGB))
	defer r.Close()

	hdr, err := r.Header()
	if err != nil {
		panic(err)
	}
	fmt.Println(flatgeobuf.HeaderString(hdr))

	// Read the index into memory. This is a good option if repeated index
	// searches are planned.
	index, _ := r.Index()
	fmt.Println("Index ->", index)

	// Search the index for features intersecting a bounding box.
	results := index.Search(packedrtree.Box{
		XMin: -81.73195714597884, YMin: 47.667150959664525,
		XMax: -81.71291285629297, YMax: 47.67849844412743,
	})
	fmt.Printf("Results -> %+v\n", results)

	// Read the search results, and print the properties for the first
	// intersecting result.
	if len(results) > 0 {
		sort.Sort(results)
		data := make([]flat.Feature, results[0].RefIndex+1)
		n, _ := r.Data(data) // Ignoring error to simplify example only!
		if n > results[0].RefIndex {
			fmt.Printf("First Result: %s\n", flatgeobuf.FeatureString(&data[results[0].RefIndex], hdr))
		}
	}
}
Output:

Header{Name:countries,Envelope:[-180,-85.609038,180,83.64513],Type:MultiPolygon,Columns:2,Features:179,NodeSize:16,CRS:{Org:EPSG,Code:4326,Name:WGS 84,WKT:354 bytes}}
Index -> PackedRTree{Bounds:[-180,-85.609038,180,83.64513],NumRefs:179,NodeSize:16}
Results -> [{Offset:147776 RefIndex:162} {Offset:160424 RefIndex:165} {Offset:167864 RefIndex:166}]
First Result: Feature{Geometry:{Type:MultiPolygon,Bounds:[-180,41.151416,180,81.2504],Parts:13},Properties:{id:RUS,name:Russia}}

func (*FileReader) IndexSearch

func (r *FileReader) IndexSearch(b packedrtree.Box) ([]flat.Feature, error)

IndexSearch searches the FlatGeobuf file's index and returns the FlatBuffer table corresponding to each data section feature whose bounding box intersects the query box. If the FlatGeobuf file has no index, the error ErrNoIndex is returned.

This method may only be called immediately after a successful call to Header or Rewind.

If the underlying stream passed to NewFileReader implements the io.Seeker interface, IndexSearch will perform a streaming search of the index without needing to read the whole index into memory. This allows efficient searches of random access capable streams, including HTTP streams using HTTP range requests. Again if io.Seeker is implemented, repeated streaming searches are enabled by calling Rewind after each call to IndexSearch.

Example (Streaming)
package main

import (
	"bytes"
	"compress/bzip2"
	"encoding/base64"
	"fmt"
	"io"
	"strings"

	"github.com/gogama/flatgeobuf/flatgeobuf"
	"github.com/gogama/flatgeobuf/flatgeobuf/flat"

	testdata "github.com/gogama/flatgeobuf/flatgeobuf/testdata/flatgeobuf"
	"github.com/gogama/flatgeobuf/packedrtree"
)

func testdataReader(data string) io.Reader {
	var r io.Reader
	r = strings.NewReader(data)
	r = base64.NewDecoder(base64.StdEncoding, r)
	r = bzip2.NewReader(r)
	b, err := io.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return bytes.NewReader(b)
}

func main() {
	// This example demonstrates a streaming index search of a
	// FlatGeobuf file which contains an index. The FileReader's
	// IndexSearch() function reads and searches the index and fetches
	// all candidate features in a streaming manner, reading only the
	// minimum necessary data into memory.

	r := flatgeobuf.NewFileReader(testdataReader(testdata.UScountiesFGB))
	defer r.Close()

	hdr, err := r.Header()
	if err != nil {
		panic(err)
	}
	fmt.Println(flatgeobuf.HeaderString(hdr))

	var data []flat.Feature

	// First search: Cook County, IL.
	if data, err = r.IndexSearch(packedrtree.Box{
		XMin: -87.63429124101445, YMin: 41.87174069508944,
		XMax: -87.61485750565028, YMax: 41.88406678494189,
	}); err != nil || len(data) == 0 {
		panic(fmt.Sprintf("err=  %v, len(data) = %d", err, len(data)))
	}
	fmt.Printf("First search, first Result: %s\n", flatgeobuf.FeatureString(&data[0], hdr))

	// Rewind.
	if err = r.Rewind(); err != nil {
		panic(err)
	}

	// Second search: Maricopa County, AZ.
	if data, err = r.IndexSearch(packedrtree.Box{
		XMin: -112.10457517582745, YMin: 33.43241637947986,
		XMax: -112.03936601127879, YMax: 33.46045877551812,
	}); err != nil || len(data) == 0 {
		panic(fmt.Sprintf("err=  %v, len(features) = %d", err, len(data)))
	}
	fmt.Printf("Second search, first Result: %s\n", flatgeobuf.FeatureString(&data[0], hdr))
}
Output:

Header{Name:US__counties,Envelope:[-179.14734,17.884813,179.77847,71.352561],Type:Unknown,Columns:6,Features:3221,NodeSize:16,CRS:{Org:EPSG,Code:4269,Name:NAD83,WKT:1280 bytes}}
First search, first Result: Feature{Geometry:{Type:MultiPolygon,Bounds:[-88.263572,41.469555,-87.524044,42.154265],Parts:1},Properties:{STATE_FIPS:17,COUNTY_FIP:031,FIPS:17031,STATE:IL,NAME:Cook,LSAD:County}}
Second search, first Result: Feature{Geometry:{Type:MultiPolygon,Bounds:[-113.33438,32.504938,-111.03991,34.04817],Parts:1},Properties:{STATE_FIPS:04,COUNTY_FIP:013,FIPS:04013,STATE:AZ,NAME:Maricopa,LSAD:County}}

func (*FileReader) Rewind

func (r *FileReader) Rewind() error

Rewind seeks the read position of the underlying stream to the position directly after the FlatGeobuf header buffer, enabling repeat calls to IndexSearch, Index, Data, or DataRem. Returns ErrNotSeekable if the underlying stream does not implement io.Seeker.

This method may only be called once Header has been called.

type FileWriter

type FileWriter struct {
	// contains filtered or unexported fields
}

FileWriter writes a FlatGeobuf file to an underlying stream.

func NewFileWriter

func NewFileWriter(w io.Writer) *FileWriter

NewFileWriter creates a new FlatGeobuf file writer based on an underlying output stream.

The underlying writer must be positioned at the beginning of the file, i.e. right before the FlatGeobuf magic number.

func (*FileWriter) Close

func (w *FileWriter) Close() error

Close closes the FileWriter. All subsequent calls to any method will return ErrClosed.

If the underlying stream implements io.Closer, this method invokes Close on the underlying stream and returns the result.

func (*FileWriter) Data

func (w *FileWriter) Data(p []flat.Feature) (n int, err error)

Data writes features into the data section. If the feature count field written with Header is non-zero, then the input feature count, plus the count of features already written, may not exceed file feature count. The total number of bytes written is returned.

This method may only be called after Header has been called. If a positive index node size was indicated with Header, then it may only be called after Index has been called. This method may be called repeatedly to stream as many features as desired into the data section, as long as total number of features written does not exceed a positive feature count written with Header.

The input features are FlatBuffer tables. Each feature must be a size-prefixed root table positioned at offset 0 within its buffer. This type of value is returned by FileReader.Data, FileReader.DataRem, and from flat.GetSizePrefixedRootAsFeature.

func (*FileWriter) Header

func (w *FileWriter) Header(hdr *flat.Header) (n int, err error)

Header writes the FlatGeobuf file magic number, followed by the given FlatGeobuf header structure. The total number of bytes written, including magic number and header bytes, is returned.

The input header table must be a size-prefixed root FlatBuffer table positioned at offset 0 within its FlatBuffer. This type of value is returned by FileReader.Header or from flat.GetSizePrefixedRootAsHeader.

This method may only be called once, immediately after creating the FileWriter via NewFileWriter.

func (*FileWriter) Index

func (w *FileWriter) Index(index *packedrtree.PackedRTree) (n int, err error)

Index serializes and writes an in-memory FlatGeobuf index data structure to the index section of a FlatGeobuf file. The index node size and feature count must match the corresponding header fields written with Header. The total number of bytes written is returned.

If used, this method must be called immediately after a successful call to Header, and may only be called once. Alternatively, the IndexData method may be used, or the index may be skipped and Data may be called directly after Header.

func (*FileWriter) IndexData

func (w *FileWriter) IndexData(p []flat.Feature) (n int, err error)

IndexData generates and writes an index for the given feature list, to the index section of a FlatGeobuf file, and then writes the features themselves into the data section. The input feature count must match the feature count header field written with Header. The total number of bytes written, to both index and data sections, is returned.

If used, this method must be called immediately after a successful call to Header, and may only be called once. Alternatively, the Index method may be used if you already have an index data structure ready to serializeIndex, or the index may be skipped and Data may be called directly after Header.

The input features are FlatBuffer tables. Each feature must be a size-prefixed root table positioned at offset 0 within its buffer. This type of value is returned by FileReader.Data, FileReader.DataRem, and from flat.GetSizePrefixedRootAsFeature.

type PropReader

type PropReader struct {
	// contains filtered or unexported fields
}

PropReader reads a list of key/value pairs in FlatGeobuf property format from an underlying stream.

Each FlatGeobuf feature table (flat.Feature) contains an optional byte array field named Properties which is encoded in its own custom format, a format-within-a-format, if you will. PropReader knows how to read this special format-within-a-format.

To read all properties at once for a given feature property Schema, use ReadSchema.

Use ReadString for flat.ColumnTypeString and flat.ColumnTypeDateTime. Use ReadBinary for flat.ColumnTypeBinary and flat.ColumnTypeJson.

Example
package main

import (
	"bytes"
	"encoding/hex"
	"fmt"

	"github.com/gogama/flatgeobuf/flatgeobuf"
)

func main() {
	// Start with a byte buffer containing three trivial properties.
	//
	// Normally you would obtain this buffer using the PropertiesBytes()
	// method of a flat.Feature, but we omit that part for simplicity.
	propBytes, _ := hex.DecodeString("000003000000666f6f010024082020020001")

	// Read the three properties. Error handling is omitted for brevity.
	//
	// If your column schema can vary, or you just want a simpler
	// interface to read properties, you may want to use the ReadSchema
	// method to read all properties at once.
	pr := flatgeobuf.NewPropReader(bytes.NewReader(propBytes))
	col, _ := pr.ReadUShort() // Column number
	str, _ := pr.ReadString() // Property value
	fmt.Printf("Column %d is the string value %q\n", col, str)
	col, _ = pr.ReadUShort() // Column number
	u, _ := pr.ReadUInt()    // Property value
	fmt.Printf("Column %d is the unsigned integer value 0x%x\n", col, u)
	col, _ = pr.ReadUShort() // Column number
	b, _ := pr.ReadBool()    // Property value
	fmt.Printf("Column %d is the boolean value %t\n", col, b)

}
Output:

Column 0 is the string value "foo"
Column 1 is the unsigned integer value 0x20200824
Column 2 is the boolean value true

func NewPropReader

func NewPropReader(r io.Reader) *PropReader

NewPropReader creates a new FlatGeobuf feature property reader reading from an underlying input stream.

func (*PropReader) ReadBinary

func (r *PropReader) ReadBinary() ([]byte, error)

ReadBinary reads an arbitrary-length property value, which can be either flat.ColumnTypeBinary or a flat.ColumnTypeJson.

func (*PropReader) ReadBool

func (r *PropReader) ReadBool() (bool, error)

ReadBool reads the value of a flat.ColumnTypeBool property (a byte value of zero for false, one for true).

func (*PropReader) ReadByte

func (r *PropReader) ReadByte() (int8, error)

ReadByte reads the value of a flat.ColumnTypeByte property (signed byte value).

func (*PropReader) ReadDouble

func (r *PropReader) ReadDouble() (float64, error)

ReadDouble reads the value of a flat.ColumnTypeDouble property (an IEEE 64-bit double precision floating point number).

func (*PropReader) ReadFloat

func (r *PropReader) ReadFloat() (float32, error)

ReadFloat reads the value of a flat.ColumnTypeFloat property (an IEEE 32-bit single precision floating point number).

func (*PropReader) ReadInt

func (r *PropReader) ReadInt() (int32, error)

ReadInt writes the value of a flat.ColumnTypeInt property (a 32-bit signed integer value).

func (*PropReader) ReadLong

func (r *PropReader) ReadLong() (int64, error)

ReadLong reads the value of a flat.ColumnTypeLong property (a 64-bit signed integer value).

func (*PropReader) ReadSchema

func (r *PropReader) ReadSchema(schema Schema) ([]PropValue, error)

ReadSchema all properties specified in the given Schema, returning them as a slice of PropValue structures.

The concrete implementation of the schema will typically be a *flat.Header or a *flat.Feature.

Example
package main

import (
	"bytes"
	"encoding/hex"
	"fmt"

	"github.com/gogama/flatgeobuf/flatgeobuf"
	"github.com/gogama/flatgeobuf/flatgeobuf/flat"

	flatbuffers "github.com/google/flatbuffers/go"
)

func simpleHeader() *flat.Header {

	bldr := flatbuffers.NewBuilder(0)

	col0Name := bldr.CreateString("A string")
	col1Name := bldr.CreateString("An unsigned int")
	col2Name := bldr.CreateString("A bool")

	flat.ColumnStart(bldr)
	flat.ColumnAddName(bldr, col0Name)
	flat.ColumnAddType(bldr, flat.ColumnTypeString)
	col0 := flat.ColumnEnd(bldr)

	flat.ColumnStart(bldr)
	flat.ColumnAddName(bldr, col1Name)
	flat.ColumnAddType(bldr, flat.ColumnTypeUInt)
	col1 := flat.ColumnEnd(bldr)

	flat.ColumnStart(bldr)
	flat.ColumnAddName(bldr, col2Name)
	flat.ColumnAddType(bldr, flat.ColumnTypeBool)
	col2 := flat.ColumnEnd(bldr)

	flat.HeaderStartColumnsVector(bldr, 3)
	bldr.PrependUOffsetT(col2)
	bldr.PrependUOffsetT(col1)
	bldr.PrependUOffsetT(col0)
	cols := bldr.EndVector(3)

	flat.HeaderStart(bldr)
	flat.HeaderAddColumns(bldr, cols)
	hdr := flat.HeaderEnd(bldr)
	flat.FinishSizePrefixedHeaderBuffer(bldr, hdr)
	return flat.GetSizePrefixedRootAsHeader(bldr.FinishedBytes(), 0)
}

func main() {
	// Get an example FlatGeobuf file header. Both *flat.Header and
	// *flat.Feature implement Schema and can be used with ReadSchema.
	hdr := simpleHeader()

	// Start with a byte buffer containing three trivial properties
	// which follows the schema from the above header.
	//
	// Normally you would obtain this buffer using the PropertiesBytes()
	// method of a flat.Feature, but we omit that part for simplicity.
	propBytes, _ := hex.DecodeString("000003000000666f6f010024082020020001")

	// Read the properties.
	pr := flatgeobuf.NewPropReader(bytes.NewReader(propBytes))
	vals, _ := pr.ReadSchema(hdr)

	// Print the properties.
	fmt.Println(vals[0])
	fmt.Println(vals[1])
	fmt.Println(vals[2])
}
Output:

PropValue{Name:"A string",Type:String,Value:"foo",ColIndex:0}
PropValue{Name:"An unsigned int",Type:UInt,Value:0x20200824,ColIndex:1}
PropValue{Name:"A bool",Type:Bool,Value:true,ColIndex:2}

func (*PropReader) ReadShort

func (r *PropReader) ReadShort() (int16, error)

ReadShort reads the value of a flat.ColumnTypeShort property (a 16-bit signed integer value).

func (*PropReader) ReadString

func (r *PropReader) ReadString() (string, error)

ReadString reads the value of a string property of type flat.ColumnTypeString.

ReadString can also be used to read a value of type flat.ColumnTypeDateTime, since FlatGeobuf encodes date/time values as strings serialized in the ISO-8601 format.

func (*PropReader) ReadUByte

func (r *PropReader) ReadUByte() (uint8, error)

ReadUByte reads the value of a flat.ColumnTypeUByte property (unsigned byte value).

func (*PropReader) ReadUInt

func (r *PropReader) ReadUInt() (uint32, error)

ReadUInt reads the value of a flat.ColumnTypeUInt property (a 32-bit unsigned integer value).

func (*PropReader) ReadULong

func (r *PropReader) ReadULong() (uint64, error)

ReadULong reads the value of a flat.ColumnTypeULong property (a 64-bit unsigned integer value).

func (*PropReader) ReadUShort

func (r *PropReader) ReadUShort() (uint16, error)

ReadUShort reads the value of a flat.ColumnTypeUShort property (a 16-bit unsigned integer value).

type PropValue

type PropValue struct {
	// Col is the FlatGeobuf column table describing the property's
	// column, or key.
	Col flat.Column
	// Value is the deserialized property value.
	Value any
	// ColIndex is the zero-based index of the property within the
	// feature's column schema.
	ColIndex uint16
	// Type is the FlatGeobuf column type. This value is repeated in the
	// Type field for ease of consumption, and is also available through
	// Col.
	Type flat.ColumnType
}

PropValue pairs together a FlatGeobuf feature property "key" (a flat.Column reference) with the property value and the type of its value.

func (PropValue) String

func (val PropValue) String() string

type PropWriter

type PropWriter struct {
	// contains filtered or unexported fields
}

PropWriter writes a list of key/value pairs in FlatGeobuf property format to an underlying stream.

Each FlatGeobuf feature table (flat.Feature) contains an optional byte array field named Properties which is encoded in its own custom format, a format-within-a-format, if you will. PropWriter knows how to write this special format-within-a-format.

A typical usage pattern is to write the properties for a feature to a byte buffer using a PropWriter, convert the buffer containing the properties into a FlatBuffer byte vector (with flatbuffers.Builder), and finally supply the vector offset when building the feature using flat.FeatureAddProperties.

Use WriteString for flat.ColumnTypeString and flat.ColumnTypeDateTime. Use WriteBinary for flat.ColumnTypeBinary and flat.ColumnTypeJson.

Example
package main

import (
	"bytes"
	"encoding/hex"
	"fmt"

	"github.com/gogama/flatgeobuf/flatgeobuf"
	"github.com/gogama/flatgeobuf/flatgeobuf/flat"

	flatbuffers "github.com/google/flatbuffers/go"
)

func main() {
	var buf bytes.Buffer
	pw := flatgeobuf.NewPropWriter(&buf)

	// Serialize the properties to a byte buffer in the FlatGeobuf
	// properties format.
	pw.WriteUShort(0) // Column 0
	pw.WriteString("foo")
	pw.WriteUShort(1) // Column 1
	pw.WriteUInt(0x20200824)
	pw.WriteUShort(2) // Column 2
	pw.WriteBool(true)
	props := buf.Bytes()

	// Attach the properties to a FlatGeobuf feature. (Feature type and
	// geometry, which are required for a meaningful feature, are
	// omitted from this example to keep it lean.)
	bldr := flatbuffers.NewBuilder(0)
	propsOffset := bldr.CreateByteVector(props)
	flat.FeatureStart(bldr)
	flat.FeatureAddProperties(bldr, propsOffset)
	ftrOffset := flat.FeatureEnd(bldr)
	flat.FinishSizePrefixedFeatureBuffer(bldr, ftrOffset)

	fmt.Printf("props: %s, propsOffset: %d, ftrOffset: %d", hex.EncodeToString(props), propsOffset, ftrOffset)
}
Output:

props: 000003000000666f6f010024082020020001, propsOffset: 24, ftrOffset: 32

func NewPropWriter

func NewPropWriter(w io.Writer) *PropWriter

NewPropWriter creates a new FlatGeobuf feature property writer based on an underlying output stream.

func (*PropWriter) WriteBinary

func (w *PropWriter) WriteBinary(v []byte) (n int, err error)

WriteBinary writes an arbitrary-length property value, which can be either flat.ColumnTypeBinary or a flat.ColumnTypeJson.

func (*PropWriter) WriteBool

func (w *PropWriter) WriteBool(v bool) (n int, err error)

WriteBool writes the value of a flat.ColumnTypeBool property (a byte value of zero for false, one for true).

func (*PropWriter) WriteByte

func (w *PropWriter) WriteByte(v int8) (n int, err error)

WriteByte writes the value of a flat.ColumnTypeByte property (signed byte value).

func (*PropWriter) WriteDouble

func (w *PropWriter) WriteDouble(v float64) (n int, err error)

WriteDouble writes the value of a flat.ColumnTypeDouble property (an IEEE 64-bit double precision floating point number).

func (*PropWriter) WriteFloat

func (w *PropWriter) WriteFloat(v float32) (n int, err error)

WriteFloat writes the value of a flat.ColumnTypeFloat property (an IEEE 32-bit single precision floating point number).

func (*PropWriter) WriteInt

func (w *PropWriter) WriteInt(v int32) (n int, err error)

WriteInt writes the value of a flat.ColumnTypeInt property (a 32-bit signed integer value).

func (*PropWriter) WriteLong

func (w *PropWriter) WriteLong(v int64) (n int, err error)

WriteLong writes the value of a flat.ColumnTypeLong property (a 64-bit signed integer value).

func (*PropWriter) WriteShort

func (w *PropWriter) WriteShort(v int16) (n int, err error)

WriteShort writes the value of a flat.ColumnTypeShort property (a 16-bit signed integer value).

func (*PropWriter) WriteString

func (w *PropWriter) WriteString(v string) (n int, err error)

WriteString writes the value of a string property of type flat.ColumnTypeString.

WriteString can also be used to write a value of type flat.ColumnTypeDateTime provided the value is serialized to a string in the ISO-8601 format.

func (*PropWriter) WriteUByte

func (w *PropWriter) WriteUByte(v uint8) (n int, err error)

WriteUByte writes the value of a flat.ColumnTypeUByte property (unsigned byte value).

func (*PropWriter) WriteUInt

func (w *PropWriter) WriteUInt(v uint32) (n int, err error)

WriteUInt writes the value of a flat.ColumnTypeUInt property (a 32-bit unsigned integer value).

func (*PropWriter) WriteULong

func (w *PropWriter) WriteULong(v uint64) (n int, err error)

WriteULong writes the value of a flat.ColumnTypeULong property (a 64-bit unsigned integer value).

func (*PropWriter) WriteUShort

func (w *PropWriter) WriteUShort(v uint16) (n int, err error)

WriteUShort writes the value of a flat.ColumnTypeUShort property (a 16-bit unsigned integer value).

type Schema

type Schema interface {
	// ColumnsLength returns the number of columns, i.e. properties, in
	// the schema.
	ColumnsLength() int
	// Columns obtains the metadata for the column, i.e. property, at a
	// specific index.
	//
	// The index j may range from 0 to ColumnsLength()-1. The pointer
	// obj must not be nil. On return, obj points to the property
	// metadata (flat.Column) for property j and the return value is
	// true. A return value of false indicates that no property metadata
	// was found for the property at index j.
	Columns(obj *flat.Column, j int) bool
}

Schema is a schema for FlatGeobuf's feature property format. It documents the number of available properties (ColumnsLength) for a feature and the property definition (flat.Column) for each property.

Both the header structure (flat.Header) of a FlatGeobuf file and an individual feature within the data section (flat.Feature) implement Schema. When provided on the header table, the Schema applies to all features in the data section, except those features that have their own dedicated schema. When provided on an individual feature, the Schema applies only to that feature.

Use PropReader.ReadSchema to read all properties from a FlatGeobuf properties buffer that is serialized according to a particular schema.

type SpecVersion

type SpecVersion struct {
	// Major is the major version of the FlatGeobuf specification.
	Major uint8
	// Patch is the patch version of the FlatGeobuf specification.
	Patch uint8
}

SpecVersion is a version of the FlatGeobuf specification.

func Magic

func Magic(r io.Reader) (SpecVersion, error)

Magic reads the FlatGeobuf magic number from a stream and, if it is valid, returns the FlatGeobuf specification version. This function can be used to test whether any file seems to be in the FlatGeobuf format. However, it does not read beyond the magic number.

Calling this function will result in 8 bytes being read from the stream reader (unless there were fewer than 8 bytes available, in which all available bytes in the stream are consumed).

Example
package main

import (
	"bytes"
	"compress/bzip2"
	"encoding/base64"
	"fmt"
	"io"
	"strings"

	"github.com/gogama/flatgeobuf/flatgeobuf"

	testdata "github.com/gogama/flatgeobuf/flatgeobuf/testdata/flatgeobuf"
)

func testdataReader(data string) io.Reader {
	var r io.Reader
	r = strings.NewReader(data)
	r = base64.NewDecoder(base64.StdEncoding, r)
	r = bzip2.NewReader(r)
	b, err := io.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return bytes.NewReader(b)
}

func main() {
	var r = testdataReader(testdata.Poly00FGB)

	version, err := flatgeobuf.Magic(r)
	fmt.Printf("%+v, %v\n", version, err)
}
Output:

{Major:3 Patch:0}, <nil>

Directories

Path Synopsis
Package flat provides types generated by flatc from the FlatGeobuf FlatBuffers schema.
Package flat provides types generated by flatc from the FlatGeobuf FlatBuffers schema.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL