{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "A simplified, opinionated wrapper around the ArcGIS REST API Export Map operation. Designed to expose the most commonly used parameters in a human- and AI-friendly format. Power-user parameters not covered by this schema (e.g. dynamicLayers, datumTransformations, clipping, gdbVersion, rotation, mapScale, historicMoment, selectionDefinitions, layerRangeValues, mapRangeValues, layerParameterValues, layerTimeOptions) can be passed through verbatim via the 'passthrough' property, which is merged directly into the underlying REST request. If this operation requires attribute filtering of layers, it is best to inspect all relevant layers to ensure you have the correct field names and data types for constructing valid SQL WHERE clauses.",
  "type": "object",
  "required": [
    "serviceUrl",
    "bbox",
    "bboxSR"
  ],
  "additionalProperties": false,
  "properties": {
    "serviceUrl": {
      "type": "string",
      "format": "uri",
      "description": "The full URL to the ArcGIS MapServer REST endpoint. This is the root resource URL for the map service, e.g. 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer'. The '/export' path segment is appended automatically by the wrapper."
    },
    "bbox": {
      "type": "object",
      "description": "The geographic extent (bounding box) to export, expressed as an envelope with explicit xmin, ymin, xmax, and ymax properties. Coordinates must be in the spatial reference specified by bboxSR. This structure mirrors the standard ArcGIS envelope object and is unambiguous for both human and machine callers — no need to remember coordinate order in a string or array.",
      "required": [
        "xmin",
        "ymin",
        "xmax",
        "ymax"
      ],
      "additionalProperties": false,
      "properties": {
        "xmin": {
          "type": "number",
          "description": "The minimum x-coordinate (left edge) of the bounding box. In WGS 84 (WKID 4326), this is the western longitude."
        },
        "ymin": {
          "type": "number",
          "description": "The minimum y-coordinate (bottom edge) of the bounding box. In WGS 84 (WKID 4326), this is the southern latitude."
        },
        "xmax": {
          "type": "number",
          "description": "The maximum x-coordinate (right edge) of the bounding box. In WGS 84 (WKID 4326), this is the eastern longitude."
        },
        "ymax": {
          "type": "number",
          "description": "The maximum y-coordinate (top edge) of the bounding box. In WGS 84 (WKID 4326), this is the northern latitude."
        }
      },
      "examples": [
        {
          "xmin": -104.99,
          "ymin": 39.73,
          "xmax": -104.98,
          "ymax": 39.75
        }
      ]
    },
    "bboxSR": {
      "$ref": "#/$defs/spatialReference",
      "description": "The spatial reference of the bounding box coordinates. This is mandatory because the caller may not know the native spatial reference of the target map service ahead of time, and the wrapper cannot safely assume a default. Commonly used values: WKID 4326 for WGS 84 (longitude/latitude), WKID 3857 or 102100 for Web Mercator."
    },
    "imageSR": {
      "$ref": "#/$defs/spatialReference",
      "description": "The spatial reference of the output image. If omitted, defaults to the value of bboxSR, which is the correct behavior for the vast majority of use cases. Only specify this when you need the exported image reprojected into a different coordinate system than your input extent."
    },
    "layers": {
      "type": "array",
      "description": "An array of layer configurations controlling which layers appear in the exported image and how they are filtered. Each entry targets a specific layer by its numeric ID (as reported by the MapServer root resource). If a layer is included in this array with 'visible' set to true, it will be shown; if 'visible' is false, it will be explicitly hidden. The behavior for layers NOT listed in this array is controlled by the 'layerMode' parameter.",
      "items": {
        "type": "object",
        "required": [
          "id"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 0,
            "description": "The layer ID as returned by the MapServer root resource. This is the numeric identifier for the layer within the map service."
          },
          "visible": {
            "type": "boolean",
            "default": true,
            "description": "Whether this layer should be visible in the exported image. Defaults to true, since the most common reason to list a layer is to show it (possibly with a filter)."
          },
          "where": {
            "type": "string",
            "description": "A SQL WHERE clause to filter features in this layer. Only features matching the expression will be drawn. This is applied on top of any definition expression already published with the service. Example: STATE_NAME = 'California' AND POP2007 > 50000. If omitted, all features in the layer are drawn (subject to the service's own definition expression)."
          }
        }
      }
    },
    "layerMode": {
      "type": "string",
      "enum": [
        "filter",
        "append"
      ],
      "default": "filter",
      "description": "Controls how layers NOT listed in the 'layers' array are handled. 'filter' (default): Only the layers explicitly listed in the 'layers' array are drawn. All other layers in the map service are excluded. This maps to the 'show:' directive in the underlying API. 'append': The map service's default layer visibility is preserved, and the entries in the 'layers' array are added on top as overrides (showing, hiding, or filtering specific layers). This maps to the 'include:' and 'exclude:' directives in the underlying API."
    },
    "width": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4096,
      "default": 1200,
      "description": "The width of the exported image in pixels. Defaults to 1200, which is a reasonable size for most screen and web display purposes. The ArcGIS Server default of 400 is generally too small for practical use. Note: if the aspect ratio of width/height does not match the aspect ratio of the bbox, the server will adjust the extent to prevent stretching. The actual extent is returned in the response."
    },
    "height": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4096,
      "default": 800,
      "description": "The height of the exported image in pixels. Defaults to 800. See the 'width' description for notes on aspect ratio behavior."
    },
    "format": {
      "type": "string",
      "enum": [
        "png",
        "png8",
        "png24",
        "png32",
        "jpg",
        "pdf",
        "bmp",
        "gif",
        "svg",
        "svgz"
      ],
      "default": "png",
      "description": "The image format of the exported map. Defaults to 'png', which supports transparency and is the most versatile choice. Use 'jpg' for smaller file sizes when transparency is not needed. Use 'pdf' or 'svg' for vector-quality output. Note that only 'png' and 'gif' formats support background transparency."
    },
    "transparent": {
      "type": "boolean",
      "default": true,
      "description": "Whether the background of the exported image should be transparent. Defaults to true, since the most common use case for programmatic map export is compositing layers over other content (basemaps, other images, web pages). Only effective when the 'format' is 'png' or 'gif'. The underlying ArcGIS Server default is false, but this wrapper defaults to true."
    },
    "dpi": {
      "type": "integer",
      "minimum": 1,
      "maximum": 600,
      "default": 150,
      "description": "The resolution of the exported image in dots per inch. Defaults to 150, a middle ground between screen display (96 dpi) and print quality (300 dpi). Use 96 for fast screen-only previews. Use 300 for print-quality output. Higher DPI values produce larger images and longer server response times."
    },
    "spatialFilter": {
      "type": "object",
      "description": "An optional spatial filter to restrict which features are drawn, based on a spatial relationship to a geometry. This is like 'layerDefs' but uses a spatial predicate instead of a SQL expression. Only applies to feature layers. Requires ArcGIS Server 10.8+ and the map service must report 'supportsSpatialFilter' as true. The object must conform exactly to the ArcGIS REST API spatialFilter specification.",
      "required": [
        "spatialRel",
        "geometryType",
        "geometry"
      ],
      "additionalProperties": false,
      "properties": {
        "spatialRel": {
          "type": "string",
          "enum": [
            "esriSpatialRelIntersects",
            "esriSpatialRelContains",
            "esriSpatialRelCrosses",
            "esriSpatialRelEnvelopeIntersects",
            "esriSpatialRelIndexIntersects",
            "esriSpatialRelOverlaps",
            "esriSpatialRelTouches",
            "esriSpatialRelWithin",
            "esriSpatialRelRelation"
          ],
          "default": "esriSpatialRelIntersects",
          "description": "The spatial relationship used to compare input geometry against layer features. 'esriSpatialRelIntersects' (default) is the most commonly used and returns features that intersect the input geometry in any way."
        },
        "geometryType": {
          "type": "string",
          "enum": [
            "esriGeometryPoint",
            "esriGeometryMultipoint",
            "esriGeometryPolyline",
            "esriGeometryPolygon",
            "esriGeometryEnvelope"
          ],
          "description": "The type of geometry provided in the 'geometry' property. Must match the actual shape of the geometry object."
        },
        "geometry": {
          "type": "object",
          "description": "The geometry object used for the spatial filter. Must be a valid ArcGIS REST API geometry object matching the specified geometryType. May include a 'spatialReference' property. See: https://developers.arcgis.com/rest/services-reference/enterprise/geometry-objects/"
        }
      }
    },
    "timeExtent": {
      "type": "object",
      "description": "An optional time range to filter time-aware layers. Only features falling within the specified time window will be drawn. The map service must be time-aware (check for 'timeInfo' on the service root resource). Either 'start' or 'end' may be null to represent an open-ended range (e.g. 'everything before end' or 'everything after start').",
      "additionalProperties": false,
      "properties": {
        "start": {
          "type": [
            "integer",
            "null"
          ],
          "description": "The start of the time extent as epoch milliseconds (milliseconds since January 1, 1970 UTC). Set to null for an open-ended start (i.e. everything up to 'end'). Example: 1199145600000 represents January 1, 2008 00:00:00 UTC."
        },
        "end": {
          "type": [
            "integer",
            "null"
          ],
          "description": "The end of the time extent as epoch milliseconds. Set to null for an open-ended end (i.e. everything from 'start' onward). Example: 1230768000000 represents January 1, 2009 00:00:00 UTC."
        }
      }
    },
    "responseFormat": {
      "type": "string",
      "enum": [
        "json"
      ],
      "default": "json",
      "description": "Controls what the server returns. 'json' (default): returns a JSON object containing the image URL, actual extent, width, height, and scale. The image must be fetched separately from the returned URL. This is useful when you need the metadata (especially the adjusted extent) alongside the image."
    },
    "passthrough": {
      "type": "object",
      "description": "An escape hatch for advanced ArcGIS REST API parameters not exposed as first-class properties in this schema. Any key-value pairs provided here are merged directly into the underlying Export Map REST request, with no transformation or validation by the wrapper. This is intended for power-user parameters such as dynamicLayers, datumTransformations, clipping, gdbVersion, rotation, mapScale, historicMoment, selectionDefinitions, layerRangeValues, mapRangeValues, layerParameterValues, and layerTimeOptions. Values must conform to the ArcGIS REST API Export Map specification (see: https://developers.arcgis.com/rest/services-reference/enterprise/export-map/). IMPORTANT: If a passthrough key conflicts with a parameter that the wrapper also generates from the simplified schema (e.g. providing 'bbox' or 'layers' in passthrough), the wrapper's generated value takes precedence. The passthrough is for parameters the wrapper does not otherwise produce. The ONLY exception to this rule is the 'f' parameter for response format. This will ALWAYS be set to 'json' by the wrapper, regardless of any conflicting value in passthrough, since the wrapper is designed specifically to work with JSON responses. If you need a different response format, you must call the ArcGIS REST API directly without using this wrapper.",
      "additionalProperties": true,
      "examples": [
        {
          "dynamicLayers": [
            {
              "id": 501,
              "source": {
                "type": "mapLayer",
                "mapLayerId": 0
              },
              "drawingInfo": {
                "showLabels": false
              }
            }
          ]
        },
        {
          "rotation": 45,
          "mapScale": 5000000
        },
        {
          "clipping": {
            "geometryType": "esriGeometryEnvelope",
            "geometry": {
              "xmin": -104.99,
              "ymin": 39.73,
              "xmax": -104.98,
              "ymax": 39.75,
              "spatialReference": {
                "wkid": 4326
              }
            }
          }
        }
      ]
    }
  },
  "$defs": {
    "spatialReference": {
      "description": "An ArcGIS spatial reference, specified as a full spatial reference JSON object. For common WKIDs such as 4326 (WGS84, longitude/latitude), 3857 (Web Mercator), 102100 (Web Mercator alternate), etc. use the 'wkid' property. For custom or uncommon coordinate systems, use the 'wkt' (well-known text) string.",
      "type": "object",
      "properties": {
        "wkid": {
          "type": "integer",
          "description": "The well-known ID of the spatial reference."
        },
        "latestWkid": {
          "type": "integer",
          "description": "The latest well-known ID, used when the WKID has been superseded by a newer identifier."
        },
        "wkt": {
          "type": "string",
          "description": "A well-known text (WKT) string defining a custom spatial reference. Use this when no WKID exists for your coordinate system."
        }
      }
    }
  }
}