Documentation
¶
Overview ¶
Package arcgis is a small, dependency-free client for querying ArcGIS Feature Services over their REST API.
It offers two interchangeable styles: a struct-based API (QueryParams passed to Client.Query) and a fluent builder (Client.Layer → LayerClient.Query). Pagination, counts, and object-ID-only queries are first-class.
client := arcgis.NewClient(baseURL)
features, err := client.Layer(7).Query().
Where("STAGE = 4").
Fields("NAME", "STAGE").
All(ctx)
Example (CountOnly) ¶
package main
import (
"context"
"fmt"
"log"
arcgis "github.com/richardwooding/go-arcgis"
"github.com/richardwooding/go-arcgis/capetown"
)
func main() {
client := arcgis.NewClient(capetown.BaseURL)
ctx := context.Background()
// Just get a count — no feature data transferred
count, err := client.Layer(capetown.LayerServiceRequests).
Query().
From(capetown.ServiceRequestsBySuburb("Woodstock")).
Count(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Open requests in Woodstock: %d\n", count)
}
Output:
Example (FluentStyle) ¶
package main
import (
"context"
"fmt"
"log"
arcgis "github.com/richardwooding/go-arcgis"
"github.com/richardwooding/go-arcgis/capetown"
)
func main() {
client := arcgis.NewClient(capetown.BaseURL)
ctx := context.Background()
// Fluent style — readable, chainable
features, err := client.Layer(capetown.LayerLoadSheddingBlocks).
Query().
Where("STAGE = 4").
Fields("BLOCK_NAME", "STAGE").
WithinEnvelope(18.4, -34.0, 18.6, -33.8).
PageSize(100).
All(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(features))
}
Output:
Example (InspectParams) ¶
package main
import (
"fmt"
arcgis "github.com/richardwooding/go-arcgis"
"github.com/richardwooding/go-arcgis/capetown"
)
func main() {
client := arcgis.NewClient(capetown.BaseURL)
// QueryParams is just a struct — inspect, log, or marshal it
p := client.Layer(capetown.LayerWards).
Query().
Where("YEAR = 2021").
WithoutGeometry().
Params() // returns QueryParams without executing
fmt.Printf("Layer: %d, Where: %s\n", p.LayerID, p.Where)
}
Output:
Example (NamedQuery) ¶
package main
import (
"context"
"fmt"
"log"
arcgis "github.com/richardwooding/go-arcgis"
"github.com/richardwooding/go-arcgis/capetown"
)
func main() {
client := arcgis.NewClient(capetown.BaseURL)
ctx := context.Background()
// Named CCT query — pre-built params, optionally extended
features, err := client.Layer(capetown.LayerLoadSheddingBlocks).
Query().
From(capetown.LoadSheddingBlocksForStage(4)).
WithinEnvelope(18.4, -34.0, 18.6, -33.8). // further refined
All(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(features))
}
Output:
Example (PaginatedAll) ¶
package main
import (
"context"
"fmt"
"log"
arcgis "github.com/richardwooding/go-arcgis"
"github.com/richardwooding/go-arcgis/capetown"
)
func main() {
client := arcgis.NewClient(capetown.BaseURL)
ctx := context.Background()
// QueryAll handles pagination transparently
all, err := client.QueryAll(ctx, capetown.WaterQualityResults())
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total water quality records: %d\n", len(all))
}
Output:
Example (StructStyle) ¶
package main
import (
"context"
"fmt"
"log"
arcgis "github.com/richardwooding/go-arcgis"
"github.com/richardwooding/go-arcgis/capetown"
)
func main() {
client := arcgis.NewClient(capetown.BaseURL)
ctx := context.Background()
// Struct-style — explicit, composable, serializable
features, err := client.Query(ctx, arcgis.QueryParams{
LayerID: capetown.LayerLoadSheddingBlocks,
Where: "STAGE = 4",
Fields: []string{"BLOCK_NAME", "STAGE"},
PageSize: 100,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(len(features.Features))
}
Output:
Index ¶
- type APIError
- type Client
- func (c *Client) Layer(id int) *LayerClient
- func (c *Client) LayerInfo(ctx context.Context, layerID int) (*LayerInfo, error)
- func (c *Client) Query(ctx context.Context, p QueryParams) (*FeatureSet, error)
- func (c *Client) QueryAll(ctx context.Context, p QueryParams) ([]Feature, error)
- func (c *Client) QueryCount(ctx context.Context, p QueryParams) (int, error)
- func (c *Client) QueryIDs(ctx context.Context, p QueryParams) ([]int64, error)
- func (c *Client) ServiceInfo(ctx context.Context) (*ServiceInfo, error)
- type ClientOption
- type Envelope
- type Feature
- type FeatureSet
- type Field
- type GeometryType
- type LayerClient
- type LayerInfo
- type LayerRef
- type OutputFormat
- type Point
- type QueryBuilder
- func (q *QueryBuilder) All(ctx context.Context) ([]Feature, error)
- func (q *QueryBuilder) Count(ctx context.Context) (int, error)
- func (q *QueryBuilder) Fields(fields ...string) *QueryBuilder
- func (q *QueryBuilder) First(ctx context.Context) (*FeatureSet, error)
- func (q *QueryBuilder) Format(f OutputFormat) *QueryBuilder
- func (q *QueryBuilder) From(base QueryParams) *QueryBuilder
- func (q *QueryBuilder) GroupBy(fields ...string) *QueryBuilder
- func (q *QueryBuilder) IDs(ctx context.Context) ([]int64, error)
- func (q *QueryBuilder) IntersectsPoint(x, y float64) *QueryBuilder
- func (q *QueryBuilder) Offset(n int) *QueryBuilder
- func (q *QueryBuilder) OrderBy(fields ...string) *QueryBuilder
- func (q *QueryBuilder) PageSize(n int) *QueryBuilder
- func (q *QueryBuilder) Params() QueryParams
- func (q *QueryBuilder) SpatialRel(rel SpatialRel) *QueryBuilder
- func (q *QueryBuilder) Where(clause string) *QueryBuilder
- func (q *QueryBuilder) WithinEnvelope(minX, minY, maxX, maxY float64) *QueryBuilder
- func (q *QueryBuilder) WithoutGeometry() *QueryBuilder
- type QueryParams
- type ServiceInfo
- type SpatialRel
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIError ¶
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
Details []string `json:"details,omitempty"`
}
APIError is an error returned by an ArcGIS service. ArcGIS commonly reports failures with an HTTP 200 status and an error envelope in the body, so this is surfaced even on otherwise-successful responses.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the main entry point for interacting with an ArcGIS Feature Service.
func NewClient ¶
func NewClient(baseURL string, opts ...ClientOption) *Client
NewClient creates a new ArcGIS Feature Service client. baseURL should be the root of the FeatureServer, e.g.:
https://example.gov/arcgis/rest/services/Theme/Service/FeatureServer
func (*Client) Layer ¶
func (c *Client) Layer(id int) *LayerClient
Layer returns a LayerClient scoped to a specific layer ID.
func (*Client) Query ¶
func (c *Client) Query(ctx context.Context, p QueryParams) (*FeatureSet, error)
Query executes a single-page query and returns the raw FeatureSet.
func (*Client) QueryCount ¶
QueryCount returns the count of features matching the query.
func (*Client) ServiceInfo ¶
func (c *Client) ServiceInfo(ctx context.Context) (*ServiceInfo, error)
ServiceInfo fetches metadata about the feature service.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client.
func WithHTTPClient ¶
func WithHTTPClient(hc *http.Client) ClientOption
WithHTTPClient sets a custom HTTP client.
func WithTimeout ¶
func WithTimeout(d time.Duration) ClientOption
WithTimeout sets a request timeout on the default HTTP client. It has no effect when combined with WithHTTPClient.
func WithToken ¶
func WithToken(token string) ClientOption
WithToken sets an ArcGIS authentication token, appended to every request.
type Envelope ¶
Envelope is a bounding-box spatial filter, expressed in the layer's coordinate system (typically WGS84 longitude/latitude).
type Feature ¶
type Feature struct {
Geometry json.RawMessage `json:"geometry"`
Attributes map[string]any `json:"attributes,omitempty"` // Esri JSON
Properties map[string]any `json:"properties,omitempty"` // GeoJSON
}
Feature represents a single ArcGIS feature with geometry and attributes.
The geometry is left as raw JSON because its shape depends on the requested format (Esri JSON vs GeoJSON) and the layer's geometry type. Attributes are populated for Esri JSON responses; Properties for GeoJSON.
type FeatureSet ¶
type FeatureSet struct {
Features []Feature `json:"features"`
ExceededTransferLimit bool `json:"exceededTransferLimit"`
ObjectIDFieldName string `json:"objectIdFieldName,omitempty"`
Fields []Field `json:"fields,omitempty"`
}
FeatureSet is a collection of features returned from a query.
type Field ¶
type Field struct {
Name string `json:"name"`
Type string `json:"type"`
Alias string `json:"alias"`
}
Field describes a single attribute field in a layer.
type GeometryType ¶
type GeometryType string
GeometryType for spatial filter inputs.
const ( GeometryTypeEnvelope GeometryType = "esriGeometryEnvelope" GeometryTypePoint GeometryType = "esriGeometryPoint" GeometryTypePolygon GeometryType = "esriGeometryPolygon" )
Supported geometry types for spatial filter inputs.
type LayerClient ¶
type LayerClient struct {
// contains filtered or unexported fields
}
LayerClient is scoped to a single layer and provides the fluent query entry point.
func (*LayerClient) Info ¶
func (l *LayerClient) Info(ctx context.Context) (*LayerInfo, error)
Info fetches metadata for this layer.
func (*LayerClient) Query ¶
func (l *LayerClient) Query() *QueryBuilder
Query returns a QueryBuilder pre-scoped to this layer.
type LayerInfo ¶
type LayerInfo struct {
ID int `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
MaxRecordCount int `json:"maxRecordCount"`
Fields []Field `json:"fields"`
GeometryType string `json:"geometryType"`
SupportsStatistics bool `json:"supportsStatistics"`
SupportsPagination bool `json:"supportsPagination"`
}
LayerInfo contains metadata about a feature layer.
type OutputFormat ¶
type OutputFormat string
OutputFormat controls the response format from ArcGIS.
const ( // FormatGeoJSON requests RFC 7946 GeoJSON. Each feature carries its // attributes under "properties". FormatGeoJSON OutputFormat = "geojson" // FormatJSON requests Esri JSON. Each feature carries its attributes // under "attributes". FormatJSON OutputFormat = "json" // FormatPBF requests the Protocol Buffer encoding. This package decodes // JSON responses only; use FormatPBF only with a custom decoder. FormatPBF OutputFormat = "pbf" )
type QueryBuilder ¶
type QueryBuilder struct {
// contains filtered or unexported fields
}
QueryBuilder builds a QueryParams using a fluent chainable API.
func (*QueryBuilder) All ¶
func (q *QueryBuilder) All(ctx context.Context) ([]Feature, error)
All fetches all results, handling pagination automatically.
func (*QueryBuilder) Count ¶
func (q *QueryBuilder) Count(ctx context.Context) (int, error)
Count returns only the record count matching the query.
func (*QueryBuilder) Fields ¶
func (q *QueryBuilder) Fields(fields ...string) *QueryBuilder
Fields sets the output fields to return.
func (*QueryBuilder) First ¶
func (q *QueryBuilder) First(ctx context.Context) (*FeatureSet, error)
First fetches only the first page of results.
func (*QueryBuilder) Format ¶
func (q *QueryBuilder) Format(f OutputFormat) *QueryBuilder
Format sets the response format.
func (*QueryBuilder) From ¶
func (q *QueryBuilder) From(base QueryParams) *QueryBuilder
From merges a pre-built QueryParams into this builder (useful for named queries). The layer ID set by Layer() is preserved.
func (*QueryBuilder) GroupBy ¶
func (q *QueryBuilder) GroupBy(fields ...string) *QueryBuilder
GroupBy sets the GROUP BY fields (used with statistics queries).
func (*QueryBuilder) IDs ¶
func (q *QueryBuilder) IDs(ctx context.Context) ([]int64, error)
IDs returns only the object IDs matching the query.
func (*QueryBuilder) IntersectsPoint ¶
func (q *QueryBuilder) IntersectsPoint(x, y float64) *QueryBuilder
IntersectsPoint sets a point spatial filter using the default "intersects" relationship.
func (*QueryBuilder) Offset ¶
func (q *QueryBuilder) Offset(n int) *QueryBuilder
Offset sets the starting record offset for pagination.
func (*QueryBuilder) OrderBy ¶
func (q *QueryBuilder) OrderBy(fields ...string) *QueryBuilder
OrderBy sets the ORDER BY fields.
func (*QueryBuilder) PageSize ¶
func (q *QueryBuilder) PageSize(n int) *QueryBuilder
PageSize sets the number of records per page.
func (*QueryBuilder) Params ¶
func (q *QueryBuilder) Params() QueryParams
Params returns the underlying QueryParams for inspection or reuse.
func (*QueryBuilder) SpatialRel ¶
func (q *QueryBuilder) SpatialRel(rel SpatialRel) *QueryBuilder
SpatialRel overrides the spatial relationship applied to the geometry filter.
func (*QueryBuilder) Where ¶
func (q *QueryBuilder) Where(clause string) *QueryBuilder
Where sets the SQL WHERE clause.
func (*QueryBuilder) WithinEnvelope ¶
func (q *QueryBuilder) WithinEnvelope(minX, minY, maxX, maxY float64) *QueryBuilder
WithinEnvelope sets a bounding-box spatial filter.
func (*QueryBuilder) WithoutGeometry ¶
func (q *QueryBuilder) WithoutGeometry() *QueryBuilder
WithoutGeometry omits geometry from the response (faster for attribute-only queries).
type QueryParams ¶
type QueryParams struct {
LayerID int
Where string
Fields []string
Envelope *Envelope
Geometry *Point
GeometryType GeometryType
SpatialRel SpatialRel
OrderByFields []string
GroupByFields []string
ResultOffset int
PageSize int
ReturnGeometry *bool // nil = server default (true)
ReturnIDsOnly bool
ReturnCountOnly bool
Format OutputFormat
}
QueryParams defines all parameters for an ArcGIS feature query. It can be used directly (struct-style) or built via QueryBuilder (fluent-style).
The zero value is usable: defaults are applied for an unset Where clause ("1=1"), Format (GeoJSON), and PageSize (1000).
type ServiceInfo ¶
type ServiceInfo struct {
ServiceDescription string `json:"serviceDescription"`
Layers []LayerRef `json:"layers"`
Tables []LayerRef `json:"tables"`
}
ServiceInfo contains metadata about a feature service.
type SpatialRel ¶
type SpatialRel string
SpatialRel defines the spatial relationship for geometry filters.
const ( SpatialRelIntersects SpatialRel = "esriSpatialRelIntersects" SpatialRelContains SpatialRel = "esriSpatialRelContains" SpatialRelWithin SpatialRel = "esriSpatialRelWithin" SpatialRelTouches SpatialRel = "esriSpatialRelTouches" SpatialRelOverlaps SpatialRel = "esriSpatialRelOverlaps" SpatialRelEnvelopeIntersects SpatialRel = "esriSpatialRelEnvelopeIntersects" )
Supported spatial relationships for geometry filters.