georest.esri.services

ArcGIS / Esri service query helpers: tile URL construction for Image/Map Services, and GeoJSON feature fetching for Feature Services.

Ported from geeViz’s esriLib.py, with the geeViz-viewer rendering calls (gv.Map.addTileLayer / gv.Map.addLayer) removed - these functions return data (a tile URL template, a GeoJSON dict) instead of rendering it anywhere.

Quick start:

from georest.esri import services

# Image/Map Service tile URL template
tile_url = services.getImageServiceTileUrl("https://.../ImageServer")

# Feature Service query (pre-flight count + fetch), like edw.query_features
# but for arbitrary (non-EDW) ArcGIS Feature Services
geojson = services.queryFeatureService(
    "https://services.arcgis.com/.../FeatureServer/0",
    where="STATE='UT'",
    max_features=2000,
)

Copyright 2026 Ryan Rock and Ian Housman

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Functions

computeStatisticsHistograms(url_or_result, ...)

Compute per-band pixel statistics and a value histogram over an area of an ArcGIS Image Service.

exportImage(url_or_result, bbox[, bbox_sr, ...])

Export a rendered image from an ArcGIS Image Service.

getImageServiceTileUrl(url_or_result[, token])

Build the {z}/{y}/{x} tile URL template for an ArcGIS Image Service or cached Map Service.

getLayerInfo(url_or_result[, token, timeout])

Get detailed metadata for a single service layer: fields, geometry type, and capabilities.

getSamples(url_or_result, points[, in_sr, ...])

Sample pixel values at multiple points on an ArcGIS Image Service.

getSupportedOperations(url_or_result[, ...])

List every REST operation actually exposed by a service or layer.

identifyPixelValue(url_or_result, x, y[, ...])

Identify the pixel value at a single point on an ArcGIS Image Service.

queryBoundary(url_or_result[, mosaic_rule, ...])

Get the true coverage boundary of an ArcGIS Image Service.

queryFeatureService(url_or_result[, where, ...])

Fetch features from an ArcGIS Feature Service, Map Service, or mosaic layer as GeoJSON, optionally filtered by spatial intersection.

queryFeatureServiceCount(url_or_result[, ...])

Return the number of features matching where (and an optional spatial filter) on a Feature Service, Map Service, or mosaic layer.

georest.esri.services.getImageServiceTileUrl(url_or_result: str | dict, token: str | None = None) → str[source]

Build the {z}/{y}/{x} tile URL template for an ArcGIS Image Service or cached Map Service.

Note

ArcGIS tile URLs use {z}/{y}/{x} order (y before x), not the XYZ standard {z}/{x}/{y}.

Warning

This is pure string construction - no request is made, and nothing is validated. In particular it does not check that the service is actually tiled, and it cannot: a service with no tile cache yields a well-formed template whose tiles all answer HTTP 404 (verified against a live, uncached Image Service). Only cached services serve /tile. Confirm with ?f=json first - a tiled service reports tileInfo (and a Map Service also reports singleFusedMapCache: true); an uncached Image Service reports neither and must be rendered through exportImage() instead.

Parameters:
  • url_or_result (str or dict) – Service URL, or a portal.searchPortal() result dict.

  • token (str, optional) – ArcGIS token appended to tile requests as ?token=<>.

Returns:

Tile URL template, e.g. "https://.../ImageServer/tile/{z}/{y}/{x}".

Return type:

str

Raises:
  • TypeError – If url_or_result is neither a string nor a dict.

  • ValueError – If a result dict carries no url key.

georest.esri.services.queryFeatureServiceCount(url_or_result: str | dict, where: str = '1=1', geometry: dict | str | None = None, geometry_type: str = 'esriGeometryEnvelope', spatial_rel: str = 'esriSpatialRelIntersects', in_sr: int = 4326, token: str | None = None, timeout: int | None = None) → int[source]

Return the number of features matching where (and an optional spatial filter) on a Feature Service, Map Service, or mosaic layer.

Parameters:
  • url_or_result (str or dict) – Layer URL (e.g. ".../FeatureServer/0", ".../MapServer/160"), or a portal.searchPortal() result dict.

  • where (str, optional) – SQL WHERE clause. Defaults to "1=1".

  • geometry – Spatial filter geometry. Can be a GeoJSON geometry dict, an Esri JSON geometry dict/string, a bbox string "xmin,ymin,xmax,ymax", or None for no spatial filter.

  • geometry_type (str, optional) – Esri geometry type, e.g. esriGeometryEnvelope, esriGeometryPolygon.

  • spatial_rel (str, optional) – Spatial relationship. Defaults to esriSpatialRelIntersects.

  • in_sr (int, optional) – Spatial reference WKID of geometry. Defaults to 4326 (WGS84).

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60 - some services with large mosaic catalogs can genuinely take longer than that to respond (verified against a live service); pass a larger value rather than assuming a hang.

Returns:

The number of matching features. 0 if the service answers without a count key at all.

Return type:

int

Raises:
  • RuntimeError – If the service is unreachable or answers with an HTTP error status. Note this is the transport-level failure mode for every function in this module - _http converts both urllib.error.HTTPError and urllib.error.URLError into RuntimeError, so a dead host and a 500 surface the same way.

  • ValueError – If the service answers 200 with an Esri error object in the body (e.g. an invalid where clause), or with a body that isn’t JSON at all (what a degraded HTML error page looks like).

Example:

from georest.esri import services

quakes = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Earthquakes_Since1970/FeatureServer/0"
services.queryFeatureServiceCount(quakes, where="magnitude >= 8")   # how many before fetching
georest.esri.services.queryFeatureService(url_or_result: str | dict, where: str = '1=1', geometry: dict | str | None = None, geometry_type: str = 'esriGeometryEnvelope', spatial_rel: str = 'esriSpatialRelIntersects', out_fields: str = '*', max_features: int = 1000, out_sr: int = 4326, token: str | None = None, timeout: int | None = None) → dict[str, Any][source]

Fetch features from an ArcGIS Feature Service, Map Service, or mosaic layer as GeoJSON, optionally filtered by spatial intersection.

Performs a returnCountOnly=true pre-flight (honoring the same spatial filter) before fetching geometry. If the result count exceeds max_features, a ValueError is raised with a remediation message instead of fetching a huge payload.

Parameters:
  • url_or_result (str or dict) – Layer URL (e.g. ".../FeatureServer/0", ".../MapServer/160" for a mosaic layer), or a portal.searchPortal() result dict. If the URL points to the FeatureServer root rather than a specific layer, /0 is appended automatically.

  • where (str, optional) – SQL WHERE clause sent to the service for server-side filtering. Defaults to "1=1" (all features).

  • geometry – Spatial filter geometry. Can be a GeoJSON geometry dict, an Esri JSON geometry dict/string, a bbox string "xmin,ymin,xmax,ymax", or None for no spatial filter.

  • geometry_type (str, optional) – Esri geometry type, e.g. esriGeometryEnvelope, esriGeometryPolygon.

  • spatial_rel (str, optional) – Spatial relationship. Defaults to esriSpatialRelIntersects.

  • out_fields (str, optional) – Comma-separated field names or "*".

  • max_features (int, optional) – Hard cap on feature count. Defaults to 1000.

  • out_sr (int, optional) – Output spatial reference WKID. Also used as the input spatial reference for geometry. Defaults to 4326 (WGS84).

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60 - some services with large mosaic catalogs can genuinely take longer than that to respond; pass a larger value rather than assuming a hang.

Returns:

GeoJSON FeatureCollection.

Note

Feature id values differ depending on which of the two response paths served the request - verified against live services. On the normal f=geojson path the ids are whatever the server emitted (typically the layer’s integer OBJECTIDs, e.g. 20), and property names are passed through untouched. On the f=json fallback path (see below) ids are filled in client-side from the feature’s object-id attribute (OBJECTID, FID …), falling back to its position only when it has none, and properties whose names contain a dot (e.g. SHAPE.LEN) are dropped, since many consumers reject dotted keys. Polygons on that path are split by ring winding, as on f=geojson: a multipart polygon comes back as a MultiPolygon and holes stay holes.

Return type:

dict

Raises:
  • ValueError – If the feature count exceeds max_features, or the service answers 200 with an Esri error object in the body, or with a body that isn’t JSON at all.

  • RuntimeError – If the service is unreachable or answers with an HTTP error status (see queryFeatureServiceCount()).

georest.esri.services.getLayerInfo(url_or_result: str | dict, token: str | None = None, timeout: int | None = None) → dict[str, Any][source]

Get detailed metadata for a single service layer: fields, geometry type, and capabilities.

Works for any ArcGIS layer URL - a FeatureServer/MapServer sub-layer, or an Image Service mosaic layer (e.g. .../MapServer/160).

Parameters:
  • url_or_result (str or dict) – Layer URL (e.g. ".../MapServer/160", ".../FeatureServer/0"), or a portal.searchPortal() result dict.

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60.

Returns:

Keys: name, geometryType, description, fields (list of {name, type, alias}), extent, maxRecordCount, capabilities (list of supported operations, e.g. ["Map", "Query"]), advancedQueryCapabilities, supportedQueryFormats.

An ImageServer root URL is a valid target and returns the mosaic catalog’s own fields; geometryType is empty for it, since the service root has no single geometry type of its own.

Return type:

dict

Raises:
  • ValueError – If url points at a container rather than a queryable layer - either a group layer or a MapServer/FeatureServer service root - in which case the message names the sub-layers to use instead. Also raised if the service answers with an Esri error object, or with a body that isn’t JSON.

  • RuntimeError – If the URL is unreachable or answers with an HTTP error status (see queryFeatureServiceCount()).

Example:

from georest.esri import services

quakes = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Earthquakes_Since1970/FeatureServer/0"
info = services.getLayerInfo(quakes)
info["geometryType"], [f["name"] for f in info["fields"]]
georest.esri.services.exportImage(url_or_result: str | dict, bbox: str | dict | list | tuple, bbox_sr: int = 4326, size: tuple[int, int] = (512, 512), out_sr: int | None = None, image_format: str = 'tiff', mosaic_rule: dict | None = None, rendering_rule: dict | None = None, pixel_type: str | None = None, no_data: float | list[float] | None = None, interpolation: str = 'RSP_BilinearInterpolation', out_path: str | None = None, token: str | None = None, timeout: int | None = None) → bytes | str[source]

Export a rendered image from an ArcGIS Image Service.

Parameters:
  • url_or_result (str or dict) – ImageServer URL (e.g. ".../ImageServer"), or a portal.searchPortal() result dict.

  • bbox – Export extent, as a "xmin,ymin,xmax,ymax" string, a 4-item list/tuple, or a dict with xmin/ymin/xmax/ymax keys.

  • bbox_sr (int, optional) – Spatial reference WKID of bbox. Defaults to 4326 (WGS84).

  • size (tuple of int, optional) – Output image size in pixels as (width, height). Defaults to (512, 512) - pass an explicit size for large bboxes rather than relying on the server’s own default, which can be very large.

  • out_sr (int, optional) – Output spatial reference WKID. Defaults to bbox_sr if not given.

  • image_format (str, optional) – Output image format, e.g. "tiff", "png", "png32", "jpg", "lerc". Defaults to "tiff".

  • mosaic_rule (dict, optional) – Esri mosaic rule JSON, to select which raster(s) in a mosaic dataset contribute to the export (e.g. by attribute or lock a specific raster).

  • rendering_rule (dict, optional) – Esri raster function JSON, applied server-side before export (e.g. a colormap or band math function).

  • pixel_type (str, optional) – Output pixel type, e.g. "U8", "F32".

  • no_data (float or list of float, optional) – NoData value(s), one per band.

  • interpolation (str, optional) – Resampling method, e.g. "RSP_BilinearInterpolation", "RSP_NearestNeighbor", "RSP_CubicConvolution". Defaults to "RSP_BilinearInterpolation".

  • out_path (str, optional) – If given, write the image bytes to this path and return the path instead of the raw bytes.

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60.

Returns:

The raw image bytes, or the value of out_path if given.

Return type:

bytes

Raises:
  • ValueError – If the service returns an error instead of an image - including the 200-with-a-JSON-error-body case described below - or if bbox is malformed.

  • RuntimeError – If the service is unreachable or answers with an HTTP error status (see queryFeatureServiceCount()). Note an over-large size is reported this way, as a server-side 500, rather than as a clean Esri error object.

Example:

from georest.esri import services

lcms = "https://imagery.geoplatform.gov/iipp/rest/services/Vegetation/USFS_EDW_LCMS_AnnualChange_CONUS/ImageServer"
png = services.exportImage(lcms, bbox=(-105.95, 40.5, -105.55, 40.75),
                           size=(600, 400), image_format="png")
# In a notebook: from IPython.display import Image; Image(png)
georest.esri.services.getSupportedOperations(url_or_result: str | dict, token: str | None = None, timeout: int | None = None) → list[dict[str, str]][source]

List every REST operation actually exposed by a service or layer.

ArcGIS Server does not expose the full operations list as structured JSON - the ?f=json representation only carries a coarse capabilities string (e.g. "Image,Metadata,Catalog,Mensuration"), which is not a reliable stand-in: a service can expose operations (computeStatisticsHistograms, getSamples, etc.) with no corresponding capability flag present at all. The authoritative list is only rendered on the plain HTML browse page (?f=html), as a “Supported Operations” section of links - this function fetches that page and parses those links out.

Parameters:
  • url_or_result (str or dict) – Service or layer URL, or a portal.searchPortal() result dict.

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60.

Returns:

Each entry has name (human-readable, e.g. "Export Image"), operation (URL segment, e.g. "exportImage"), and url (full operation URL).

Return type:

list of dict

Raises:

RuntimeError – If the URL is unreachable or answers with an HTTP error status - including the 404 served for a service that does not exist (see queryFeatureServiceCount()).

Example:

from georest.esri import services

lcms = "https://imagery.geoplatform.gov/iipp/rest/services/Vegetation/USFS_EDW_LCMS_AnnualChange_CONUS/ImageServer"
[op["name"] for op in services.getSupportedOperations(lcms)]
georest.esri.services.computeStatisticsHistograms(url_or_result: str | dict, geometry: dict | str, geometry_type: str = 'esriGeometryPolygon', in_sr: int = 4326, mosaic_rule: dict | None = None, rendering_rule: dict | None = None, pixel_size: dict | None = None, token: str | None = None, timeout: int | None = None) → list[dict[str, Any]][source]

Compute per-band pixel statistics and a value histogram over an area of an ArcGIS Image Service.

Uses the computeStatisticsHistograms operation rather than the narrower computeHistograms - it returns both statistics and a histogram together for the same single request, so there’s no reason to call the histogram-only operation separately.

Parameters:
  • url_or_result (str or dict) – ImageServer URL (e.g. ".../ImageServer"), or a portal.searchPortal() result dict.

  • geometry – Area to compute over. Can be a GeoJSON geometry dict, an Esri JSON geometry dict/string, a bbox string "xmin,ymin,xmax,ymax", or an Esri JSON polygon/envelope.

  • geometry_type (str, optional) – Esri geometry type, e.g. esriGeometryPolygon, esriGeometryEnvelope. Defaults to esriGeometryPolygon.

  • in_sr (int, optional) – Spatial reference WKID of geometry. Defaults to 4326 (WGS84).

  • mosaic_rule (dict, optional) – Esri mosaic rule JSON, to select which raster(s) in a mosaic dataset contribute (e.g. by attribute or lock a specific raster).

  • rendering_rule (dict, optional) – Esri raster function JSON, applied server-side before computing statistics.

  • pixel_size (dict, optional) – {"x", "y", "spatialReference"} to compute at a coarser/finer resolution than native. Omit to use the service default.

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60. At the native resolution of a high-resolution service (e.g. sub-meter imagery), this operation over any non-trivial area can exceed a server-side pixel-count limit outright - pass a coarser pixel_size to avoid that. Even with a reasonable pixel_size, a large area or a service with a big/complex mosaic catalog can legitimately take 30-45+ seconds to respond (verified against live services) - raise this rather than assuming a hang.

Returns:

One entry per band, each with band (int index), min, max, mean, stddev, sum, median, mode, count, and histogram (a dict with min, max, size, counts).

Returns an empty list - not an error - when the service computed nothing for the request. The most common cause is a pixel_size that is absurdly coarse for its own spatial reference: it is interpreted in the units of the spatialReference inside pixel_size, not the raster’s native units, so {"x": 90, "y": 90, "spatialReference": {"wkid": 4326}} asks for 90-degree pixels rather than 90-metre ones, and the service silently returns nothing (verified against a live service). Check for an empty result rather than assuming at least one band.

Return type:

list of dict

Raises:
  • ValueError – If geometry cannot be converted to Esri JSON, or the service answers with an Esri error object or a non-JSON body.

  • RuntimeError – If the service is unreachable or answers with an HTTP error status (see queryFeatureServiceCount()).

Example:

from georest.esri import services

lcms = "https://imagery.geoplatform.gov/iipp/rest/services/Vegetation/USFS_EDW_LCMS_AnnualChange_CONUS/ImageServer"
box = {"rings": [[[-105.9, 40.55], [-105.6, 40.55], [-105.6, 40.7],
                   [-105.9, 40.7], [-105.9, 40.55]]],
       "spatialReference": {"wkid": 4326}}
stats = services.computeStatisticsHistograms(lcms, box)
stats[0]["histogram"]["counts"]   # pixel count per class value
georest.esri.services.identifyPixelValue(url_or_result: str | dict, x: float, y: float, in_sr: int = 4326, mosaic_rule: dict | None = None, rendering_rule: dict | None = None, return_catalog_items: bool = False, token: str | None = None, timeout: int | None = None) → dict[str, Any][source]

Identify the pixel value at a single point on an ArcGIS Image Service.

This is inherently a point operation: the underlying identify operation accepts other geometry types (e.g. polygon), but silently collapses them to their centroid rather than sampling the area (verified against a live service), so this function only takes a single x/y rather than pretending to support area input. For area-wide pixel-value distribution, use computeStatisticsHistograms() instead.

Parameters:
  • url_or_result (str or dict) – ImageServer URL (e.g. ".../ImageServer"), or a portal.searchPortal() result dict.

  • x (float) – X coordinate (longitude, if in_sr is 4326).

  • y (float) – Y coordinate (latitude, if in_sr is 4326).

  • in_sr (int, optional) – Spatial reference WKID of x/y. Defaults to 4326 (WGS84).

  • mosaic_rule (dict, optional) – Esri mosaic rule JSON, to select which raster(s) in a mosaic dataset contribute (e.g. by attribute or lock a specific raster).

  • rendering_rule (dict, optional) – Esri raster function JSON, applied server-side before identifying the value.

  • return_catalog_items (bool, optional) – If True, also return the footprint attributes of the raster(s) covering this point (e.g. which mosaic source/year contributed) in the catalog_items result key. Defaults to False. Note: this does not control whether band_values is populated - see below.

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60 - a service with a large/complex mosaic catalog (many overlapping contributing rasters at a given point) can genuinely take 30-45+ seconds to respond (verified against a live service); pass a larger value rather than assuming a hang.

Returns:

value (str, the composited pixel value, or "NoData"), band_values (list of str, one per band/raster contributing at this point - empty where the point is NoData), location, and catalog_items (list of raster footprint {"attributes", "geometry"} dicts if return_catalog_items is True, else None).

location is {"x", "y", "spatialReference"} as returned by the service, reprojected into the service’s native spatial reference - not echoed back in in_sr. Querying a WGS84 point on a Web Mercator service returns Web Mercator metres with {"wkid": 102100} (verified against a live service), so read the spatialReference rather than assuming the coordinates come back in the units you sent.

Return type:

dict

Raises:

Example:

from georest.esri import services

lcms = "https://imagery.geoplatform.gov/iipp/rest/services/Vegetation/USFS_EDW_LCMS_AnnualChange_CONUS/ImageServer"
services.identifyPixelValue(lcms, x=-105.75, y=40.62)["value"]   # the class at one point
georest.esri.services.getSamples(url_or_result: str | dict, points: list[tuple[float, float]], in_sr: int = 4326, mosaic_rule: dict | None = None, pixel_size: dict | None = None, token: str | None = None, timeout: int | None = None) → list[dict[str, Any]][source]

Sample pixel values at multiple points on an ArcGIS Image Service.

Tries the batch getSamples operation first (one request for every point). If that fails, falls back to calling identifyPixelValue() once per point instead.

This fallback isn’t defensive boilerplate - verified against a live service, getSamples can fail outright (HTTP 400, sometimes even 500) for a perfectly valid, correctly-formatted request at specific locations (e.g. a point landing on a NoData pixel), while identify handles the identical location cleanly. Since a single bad point would otherwise cost every other point’s result too, the fallback keeps results for whichever points can succeed.

Parameters:
  • url_or_result (str or dict) – ImageServer URL (e.g. ".../ImageServer"), or a portal.searchPortal() result dict.

  • points (list of tuple) – (x, y) coordinate pairs to sample.

  • in_sr (int, optional) – Spatial reference WKID of points. Defaults to 4326 (WGS84).

  • mosaic_rule (dict, optional) – Esri mosaic rule JSON, to select which raster(s) in a mosaic dataset contribute.

  • pixel_size (dict, optional) – {"x", "y", "spatialReference"} to sample at a coarser/finer resolution than native.

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds, applied to both the batch call and any per-point identify fallback. Defaults to 60.

Returns:

One entry per input point, in the same order as points, each with x, y, value (str, or None if both the batch call and the fallback failed for this point), and source ("getSamples", "identify", or an "error: ..." string if this specific point could not be sampled at all).

This function does not raise for a point it cannot sample - a failing point is reported in-band via source so the remaining points still return their values.

x/y are in in_sr on both paths, but they do not come from the same place: on the getSamples path they are echoed back by the service, and on the identify fallback they are the input coordinates, copied through unchanged. (The fallback deliberately does not surface identifyPixelValue()’s own location, which the service returns in its native spatial reference rather than in in_sr - mixing the two would put different coordinate systems in one result list.)

Return type:

list of dict

Note

pixel_size applies only to the batch getSamples call; the identify fallback has no equivalent parameter, so a point resolved by the fallback is sampled at the service’s native resolution regardless.

Example:

from georest.esri import services

lcms = "https://imagery.geoplatform.gov/iipp/rest/services/Vegetation/USFS_EDW_LCMS_AnnualChange_CONUS/ImageServer"
points = [(-105.75, 40.62), (-105.70, 40.60), (-105.65, 40.58)]
[s["value"] for s in services.getSamples(lcms, points)]
georest.esri.services.queryBoundary(url_or_result: str | dict, mosaic_rule: dict | None = None, out_sr: int = 4326, token: str | None = None, timeout: int | None = None) → dict[str, Any][source]

Get the true coverage boundary of an ArcGIS Image Service.

Unlike the rectangular extent returned by getLayerInfo() / portal.getServiceMetadata(), this is the actual coverage shape - useful as a pre-flight check for whether a service actually covers an AOI before spending an exportImage() / computeStatisticsHistograms() call on it.

Unlike identify/computeStatisticsHistograms/getSamples, this operation does respect a plain outSR param directly (verified against a live service) - there’s no geometry input here to embed a spatial reference into in the first place.

Parameters:
  • url_or_result (str or dict) – ImageServer URL (e.g. ".../ImageServer"), or a portal.searchPortal() result dict.

  • mosaic_rule (dict, optional) – Esri mosaic rule JSON, to scope the boundary to a subset of the mosaic (e.g. one raster/year). Note: not every mosaic rule type actually changes this operation’s result - verified against a live service, a where-clause rule that should lock to a single raster returned an identical boundary to no rule at all, and the operation doesn’t validate the rule (a garbage rule is silently ignored rather than erroring).

  • out_sr (int, optional) – Output spatial reference WKID. Defaults to 4326 (WGS84).

  • token (str, optional) – ArcGIS token for secured services.

  • timeout (int, optional) – Request timeout in seconds. Defaults to 60 - a service with a large/complex mosaic catalog can genuinely take 30-45+ seconds to respond even for this operation’s simple result (verified against a live service); pass a larger value rather than assuming a hang.

Returns:

GeoJSON-shaped {"type": "Polygon", "coordinates": [...], "area": <float>}. Note: unlike the coordinates, area does not vary with out_sr - verified against a live service, it’s always reported in the service’s native storage spatial reference (e.g. square meters in Web Mercator), regardless of what SR the geometry itself was requested in.

Return type:

dict

Raises:

Example:

from georest.esri import services

lcms = "https://imagery.geoplatform.gov/iipp/rest/services/Vegetation/USFS_EDW_LCMS_AnnualChange_CONUS/ImageServer"
boundary = services.queryBoundary(lcms)   # the area the imagery actually covers