# Eindhoven municipal GIS services (ArcGIS REST)

The ArcGIS server behind the City of Eindhoven's maps: 99 map services, with a department prefix in the name (GRN_ green, VKR_ traffic, ODS_ public space, WTR_ water …). On 16 October 2026 our index held 314 layers, including 128,070 trees with species and planting year, 1,105 public charging points and 390,723 public-space reports from 2021 to August 2024. Some of these layers also feed data.eindhoven.nl, but here you query each feature directly.

- Publisher: Gemeente Eindhoven
- Area: Netherlands · Finest detail: feature (city of Eindhoven)
- Period: actuele stand (geen datums op de server) · Refreshed: yearly
- Access: Open · Licence: niet vastgesteld
- Join keys: dienst + laagnaam, OBJECTID, buurtcode van de gemeente (niet de CBS-code), RD-coördinaten (EPSG:28992)
- Status: grijs · 99 diensten · ArcGIS 11.5
- URL: https://gisservice.eindhoven.nl/arcgis/rest/services

## What can you do with it?
- **Which trees stand in your street?.** GRN_Bomen has 128,070 trees with Dutch and Latin species name, height class and planting year. The pedunculate oak is by far the most common, with 23,409 trees; the oldest planting year above 1000 is 1820 (a plane tree). There are also wrong values, such as planting year 190.
- **Charging points per neighbourhood.** VKR_Laadpalen (layer Openbare laadpalen) has 1,105 points with operator, number of connections and installation date. Count them per neighbourhood or per year of installation.
- **What do Eindhoven residents complain about?.** LOM_MOR_Laatste_Jaren holds 390,723 public-space reports (2021 to 23 August 2024) with subject, neighbourhood and status. Illegal dumping is the largest subject with 115,545 reports, followed by street lighting (26,924).
- **Oak processionary moth in the city.** The tree layer has fields on the oak processionary moth (present, stage, date found, control method). Combine with the oaks to see where the caterpillars return.

## How to process it
1. **Find the service.** All 99 map services sit in the root. The prefix tells you which department maintains it; per service, ?f=json gives the layer id.
   `https://gisservice.eindhoven.nl/arcgis/rest/services?f=json`
2. **Trees around a point, as GeoJSON.** Pass a rectangle in degrees (inSR=4326) and request output in degrees too (outSR=4326). The centre around the city hall yields 249 trees.
   `https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query?geometry=5.475,51.437,5.482,51.441&geometryType=esriGeometryEnvelope&inSR=4326&spatialRel=esriSpatialRelIntersects&outFields=BOOMSOORT_NEDERLANDS,PLANTJAAR,HOOGTE&outSR=4326&f=geojson`
3. **Count by species.** With outStatistics and groupByFieldsForStatistics the server counts for you. The count field is called N in the response, even if you ask for n.
   `https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query?where=1%3D1&outStatistics=%5B%7B%22statisticType%22%3A%22count%22%2C%22onStatisticField%22%3A%22OBJECTID%22%2C%22outStatisticFieldName%22%3A%22n%22%7D%5D&groupByFieldsForStatistics=BOOMSOORT&orderByFields=n%20DESC&f=json`
4. **Page by 2,000.** maxRecordCount is 2,000. For all trees, loop with resultOffset in steps of 2,000 (65 calls); only request the fields you need.
   `https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query?where=1%3D1&outFields=OBJECTID,BOOMSOORT,PLANTJAAR&orderByFields=OBJECTID&resultOffset=0&resultRecordCount=2000&outSR=4326&f=geojson`

## Interesting combinations
- Trees per resident per neighbourhood → [cbs-kwb](https://www.opendataindex.nl/en/bron/cbs-kwb.md) via buurtnaam of coördinaten (de buurtcode is die van de gemeente). Count trees per CBS neighbourhood (by location) and divide by population from the neighbourhood key figures.
- How tall are the trees really? → [ahn](https://www.opendataindex.nl/en/bron/ahn.md) via coördinaten. The tree layer gives a height class (e.g. 12-15 m). Compare with the AHN height at the same spot.

## Similar sources
- [data.eindhoven.nl](https://data.eindhoven.nl): The city's open data portal (Opendatasoft), with licence and downloads. Part of it is filled from this server (Openbaar groen, Plofgevoelig gebied).
- [ams-bomen](https://www.opendataindex.nl/en/bron/ams-bomen.md): The Amsterdam tree register, through its own API with similar fields.
- [ams-meldingen](https://www.opendataindex.nl/en/bron/ams-meldingen.md): Public-space reports in Amsterdam, more current and with its own API.

## What to watch out for
- **Licence not established.** The server says nothing about reuse. Eindhoven publishes on data.overheid.nl and data.eindhoven.nl mostly as public domain, but it is not stated for these layers.
- **Firewall in front of the server.** A web firewall (F5) sits in front: /robots.txt returns a 'Request Rejected' page. The REST directory itself is open; stop if you get that message and do not query too fast.
- **Not everything is current.** Layers carry no dates. The reports layer ends on 23 August 2024; 9,124 trees have no planting year or planting year 0.
- **Own neighbourhood codes.** BUURTCODE in the reports is the city's own neighbourhood number (e.g. 611), not the CBS code (BU0772…). Join on name or location.

## API (ArcGIS REST)

- Base: https://gisservice.eindhoven.nl/arcgis/rest/services
- Access: No key or token: the directory and all calls below worked anonymously. (tested)
- Formats: json, geojson, pbf, png (export, WMS)
- Size: 312 leaf layers in 99 MapServers (306 unique names), plus 8 raster layers (tested)
- Filtering: SQL-like where clause on the layer's fields, plus an optional spatial filter (geometry). Group and count with outStatistics.; =, <>, >, >=, <, <=, LIKE, IN, IS NULL, BETWEEN, AND, OR, NOT

### Endpoints
- `GET ?f=json`: Directory: version, folders and services (name + type).
- `GET /{dienst}/MapServer?f=json`: Service: layers and tables (id, name, group layer or not), maxRecordCount, capabilities, projection, description.
- `GET /{dienst}/MapServer/{laag}?f=json`: Layer: fields (name, type, alias, length), geometry type, maxRecordCount, supportedQueryFormats and advancedQueryCapabilities (pagination, statistics, orderBy, distinct).
- `GET /{dienst}/MapServer/{laag}/query`: Query features with a where clause or spatial filter; also count (returnCountOnly) and group (outStatistics). Response in JSON, GeoJSON or PBF.
- `GET /{dienst}/MapServer/legend?f=json`: Legend per layer: labels and symbols (PNG as base64).
- `GET /{dienst}/MapServer/export?bbox=…&bboxSR=4326&size=600,400&format=png&f=image`: Map image as PNG for a rectangle; with f=json you get a link to the image. Without bbox: error 'bbox must be specified'.
- `GET /{dienst}/MapServer/find?searchText=…&layers=0&searchFields=…&f=json`: Search text in fields of one or more layers (partial match), returning the layer and the matched field.
- `GET https://gisservice.eindhoven.nl/arcgis/services/{dienst}/MapServer/WMSServer?request=GetCapabilities&service=WMS`: WMS 1.3.0 of the same MapServer: path without /rest/ and with /WMSServer. Only for services whose supportedExtensions lists WMSServer.

### Parameters
- `where`: Condition in SQL-92 style: 1=1 for everything, text in single quotes (GEMEENTE='Zwolle'), LIKE with %, AND/OR, dates as DATE 'YYYY-MM-DD'. Required when there is no other filter. (tested)
- `outFields`: Fields in the response, comma-separated, or * for all fields. (tested)
- `returnGeometry`: false = attributes only (much smaller response). (tested)
- `outSR`: Projection of the geometry in the response; 4326 = degrees (WGS84). Default is the layer's projection, here RD (28992). (tested)
- `f`: Format: json (Esri JSON), geojson, pbf (protobuf), pjson (indented) or html (browse in the browser). (tested)
- `resultOffset`: Pagination: number of features to skip (from 0). Use together with orderByFields for a stable order. (tested)
- `resultRecordCount`: Maximum number of features in this response (capped at the layer's maxRecordCount). (tested)
- `orderByFields`: Sort order, e.g. 'OBJECTID' or 'n DESC' (also on a statistics field). (tested)
- `returnCountOnly`: true = only the number of features matching where: {"count": …}. (tested)
- `outStatistics`: Server-side statistics: list of {statisticType, onStatisticField, outStatisticFieldName}; statisticType count, sum, min, max, avg, stddev or var. URL-encode it. (tested)
- `groupByFieldsForStatistics`: Fields to group by with outStatistics (like GROUP BY). (tested)
- `returnDistinctValues`: true with outFields and returnGeometry=false: the unique values of those fields. (tested)
- `geometry`: Spatial filter: a point 'x,y' or envelope 'xmin,ymin,xmax,ymax', with geometryType (esriGeometryPoint, esriGeometryEnvelope), inSR (input projection), spatialRel (esriSpatialRelIntersects) and optionally distance + units (esriSRUnit_Meter) for a radius. (platform)
- `returnExtentOnly`: true = only the bounding box of the matches (with outSR=4326 in degrees). (tested)

### Fields of the example layer (10)
`OBJECTID` OID, `BOOMNUMMER` Double, `BOOMSOORT` String, `BOOMSOORT_NEDERLANDS` String, `PLANTJAAR` SmallInteger, `HOOGTE` String, `EIGENAAR` String, `BEHEERDER` String, `EPR_AANWEZIG` String, `EPR_DATUM_CONSTATERING` Date

### Limits and pitfalls
- At most 2.000 features per response (the layer's maxRecordCount; varies per layer, see /{layer}?f=json). If there are more, the response has exceededTransferLimit: true: fetch the rest with resultOffset and resultRecordCount. (tested)
- No key and no documented rate limit. Take it easy (a few requests per second) and prefer statistics or counts over fetching all features. (experience)
- This server upper-cases statistics field names: outStatisticFieldName 'n' comes back as 'N', 'gem' as 'GEM'. Read the field case-insensitively. (tested)
- resultRecordCount is ignored with outStatistics (all groups are returned, 18 KB for BOOMSOORT_NEDERLANDS). (tested)
- find searches text fields on part of the value, but returns attributes as text ('OBJECTID': '71') and is capped at 2,000 results (searchText=Tilia: 1.8 MB). For counting, prefer query with LIKE: BOOMSOORT LIKE 'Tilia%' = 7,994. (tested)
- There is an F5 firewall in front of the server: /robots.txt returns a 'Request Rejected' page (HTTP 200). The REST API itself worked fine; if you get that page on an API call, stop. (tested)
- Service names start with a department prefix (GRN_ green, VKR_ traffic, BST_, EHV_, LOM_ …). Part of data.eindhoven.nl is fed from this server. (experience)
- Errors come back with HTTP 200 and a JSON object {"error": {"code": 400, …}} ('Failed to execute query.' for an unknown field in where). Always check for an error field, not just the status code. (tested)
- The server gives no modification date per service or layer (no editingInfo). How current a layer is, is not stated. (tested)

### Example
```
https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query?where=1%3D1&outStatistics=%5B%7B%22statisticType%22%3A%22count%22%2C%22onStatisticField%22%3A%22OBJECTID%22%2C%22outStatisticFieldName%22%3A%22n%22%7D%5D&groupByFieldsForStatistics=EIGENAAR&orderByFields=n%20DESC&f=json
```

## How to fetch it

### curl
```bash
curl "https://gisservice.eindhoven.nl/arcgis/rest/services/VKR_Laadpalen/MapServer/5/query?where=1%3D1&outFields=*&outSR=4326&f=geojson"
```

### python
```python
import requests

laag = "https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query"
stat = '[{"statisticType":"count","onStatisticField":"OBJECTID","outStatisticFieldName":"n"}]'
r = requests.get(laag, params={"where": "1=1", "outStatistics": stat,
                               "groupByFieldsForStatistics": "BOOMSOORT_NEDERLANDS",
                               "orderByFields": "n DESC", "f": "json"})
for f in r.json()["features"][:10]:
    a = f["attributes"]
    print(a["BOOMSOORT_NEDERLANDS"], a["N"])
```

## Who uses it
- [data.eindhoven.nl](https://data.eindhoven.nl): The city's open data portal, partly filled from this server


## What is in it: 314 map layers

All services and layers on this ArcGIS server, fetched every night. Every layer has its own page with fields, geometry and a query call.

- Kinds: kaartlaag 304, raster 8, tabel 2
- With an API call on the dataset page: 314
- Search: https://www.opendataindex.nl/api/catalogus/arcgis-eindhoven?q=… or the MCP function zoek_datasets with herkomst=arcgis-eindhoven
- Every row has its own page: https://www.opendataindex.nl/en/dataset/arcgis-eindhoven/<key>

### Folders
- Bezoek_kaart (1)
- BWV Gebiedsindeling prioriteitennota (2)
- BWV Opslotgebieden Kamerverhuur (1)
- BWV Vergunningen (1)
- CKD Archeologie 2022 (1)
- CKD Cultuurhistorie (9)
- CKD Monumenten (6)
- EHV BGT IMGeo (45)
- EHV Luchtkwaliteit (1)
- EHV Overzichtskaart (17)
- Erfgoedkaart (11)
- Fietsenstallingen en OV-Fietsen (2)
- Gebiedskaders (1)
- Geobasis_Basis_ORAO (3)
- GRN Bomen (1)
- GRN GroeneKaart (7)
- Grondwatermeetpunten (1)
- Hoogtebouten (1)
- Hubs realisatie locatiepunt (1)
- Kansenkaart biodiversiteit (4)
- Kwaliteitsniveaus Openbare Ruimte (9)
- LOM EnergieTransitiePandBuurt (2)
- LOM GESCHUTSMUNITIE (3)
- LOM Verontreiniging (1)
- LOM WKO (4)
- LOM_BEBOUWDE_KOMGRENS (1)
- LuchtfotoActueel (1)
- LuchtfotoCache (1)
- Map (1)
- Map (2)
- Map (2)
- Map (1)
- Map (1)
- Map (1)
- ODS AFVALBAK (1)
- ODS BESTRATING (1)
- ODS BLOEMBOLLEN (1)
- ODS GOTEN (1)
- ODS HONDENLOSLOOPTERREINEN (1)
- ODS LAADPUNT (1)
- … and 59 more folders

## Examples with real figures (1)

For each example the finding, the reference date and the call that fetches exactly these figures.

- **Trees in Eindhoven's tree register by species: most zomereik (23,407 trees), then berk (Betula pendula) and gewone plataan** (november 2026). Table https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0. [Example](https://www.opendataindex.nl/en/voorbeeld/gisservices-eindhoven-bomen)
  - `https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query?where=1%3D1&outStatistics=%5B%7B%22statisticType%22%3A%22count%22%2C%22onStatisticField%22%3A%22OBJECTID%22%2C%22outStatisticFieldName%22%3A%22n%22%7D%5D&groupByFieldsForStatistics=BOOMSOORT&orderByFields=n%20DESC&f=json`
  - `https://gisservice.eindhoven.nl/arcgis/rest/services/GRN_Bomen/MapServer/0/query?where=1%3D1&returnCountOnly=true&f=json`
