UNPKG

@esri/arcgis-rest-feature-service

Version:

Feature layer query and edit helpers for @esri/arcgis-rest-js

280 lines 11.6 kB
/* Copyright (c) 2017-2018 Environmental Systems Research Institute, Inc. * Apache-2.0 */ import { request, cleanUrl, appendCustomParams, ArcGISRequestError, ArcGISAuthError } from "@esri/arcgis-rest-request"; import pbfToGeoJSON from "./pbf-parser/geoJSONPbfParser.js"; import pbfToArcGIS from "./pbf-parser/arcGISPbfParser.js"; /** * Query and decode pbf features on the client. Improves performance on slow networks and large queries. * Handles both f=pbf-as-geojson and f=pbf-as-arcgis format query params and handles errors. * * @param url - A feature service url * @param queryOptions - Options for the request that has been passed through appendCustomParams * @returns A Promise that will resolve with the query response. */ export function queryPbfAsGeoJSONOrArcGIS(url, queryOptions) { // if f=pbf-as-geojson, we need to set outSR=4326 to satisfy geojson crs standard // if f-pbf-as-geojson, outSR should not be set, or should be 4326 otherwise throw error if (queryOptions.params.f === "pbf-as-geojson" && queryOptions.params.outSR && queryOptions.params.outSR !== "4326") { throw new ArcGISRequestError("Unsupported outSR for GeoJSON requests.", 422, null, url, queryOptions); } // default pbf request to EPSG:4326 if requesting pbf-as-geojson to satisfy geojson crs standard const geoJSONSpatialReference = queryOptions.params.f === "pbf-as-geojson" ? { outSR: "4326" } : {}; // query with f=pbf and rawResponse:true on behalf of the user to fetch metadata with the pbf response const customOptions = Object.assign(Object.assign({}, queryOptions), { params: Object.assign(Object.assign(Object.assign({}, queryOptions.params), geoJSONSpatialReference), { f: "pbf" }), rawResponse: true }); return request(`${cleanUrl(url)}/query`, customOptions).then(async (response) => { var _a; // if pbf request to service returns a json format, there is an error if ((_a = response.headers.get("content-type")) === null || _a === void 0 ? void 0 : _a.includes("application/json")) { const err = (await response.json()).error; // throw auth error, else throw generic request error if ((err === null || err === void 0 ? void 0 : err.code) === 498 || (err === null || err === void 0 ? void 0 : err.code) === 499) { throw new ArcGISAuthError(err.message, err.code, response, url, customOptions); } throw new ArcGISRequestError(err.message, err.code, response, url, customOptions); } try { const arrayBuffer = await response.arrayBuffer(); /* istanbul ignore else --@preserve */ if (queryOptions.params.f === "pbf-as-arcgis") { return pbfToArcGIS(arrayBuffer); } /* istanbul ignore else --@preserve */ if (queryOptions.params.f === "pbf-as-geojson") { return pbfToGeoJSON(arrayBuffer); } } catch (error) { if (error instanceof ArcGISRequestError) { // Append response, url, customOptions error.response = response; error.url = url; error.options = customOptions; throw error; } else { // catch all for any errors that occur during the parsing of the pbf response throw new ArcGISRequestError("Unable to decode pbf response.", 500, response, url, customOptions); } } }); } /** * Get a feature by id. * * ```js * import { getFeature } from '@esri/arcgis-rest-feature-service'; * * const url = "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Landscape_Trees/FeatureServer/0"; * * getFeature({ * url, * id: 42 * }).then(feature => { * console.log(feature.attributes.FID); // 42 * }); * ``` * * @param requestOptions - Options for the request * @returns A Promise that will resolve with the feature or the [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) itself if `rawResponse: true` was passed in. */ export function getFeature(requestOptions) { const url = `${cleanUrl(requestOptions.url)}/${requestOptions.id}`; // default to a GET request const options = Object.assign({ httpMethod: "GET" }, requestOptions); return request(url, options).then((response) => { if (options.rawResponse) { return response; } return response.feature; }); } /** * Query a feature service. See [REST Documentation](https://developers.arcgis.com/rest/services-reference/query-feature-service-layer-.htm) for more information. * * ```js * import { queryFeatures } from '@esri/arcgis-rest-feature-service'; * * queryFeatures({ * url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3", * where: "STATE_NAME = 'Alaska'" * }) * .then(result) * ``` * * @param requestOptions - Options for the request * @returns A Promise that will resolve with the query response. */ export function queryFeatures(requestOptions) { var _a, _b; const queryOptions = appendCustomParams(requestOptions, [ "where", "objectIds", "relationParam", "time", "distance", "units", "outFields", "geometry", "geometryType", "spatialRel", "returnGeometry", "maxAllowableOffset", "geometryPrecision", "inSR", "outSR", "gdbVersion", "returnDistinctValues", "returnIdsOnly", "returnCountOnly", "returnExtentOnly", "orderByFields", "groupByFieldsForStatistics", "outStatistics", "returnZ", "returnM", "multipatchOption", "resultOffset", "resultRecordCount", "quantizationParameters", "returnCentroid", "resultType", "historicMoment", "returnTrueCurves", "sqlFormat", "returnExceededLimitFeatures", "f" ], { httpMethod: "GET", params: Object.assign({ // set default query parameters where: "1=1", outFields: "*" }, requestOptions.params) }); if (((_a = queryOptions.params) === null || _a === void 0 ? void 0 : _a.f) === "pbf-as-geojson" || ((_b = queryOptions.params) === null || _b === void 0 ? void 0 : _b.f) === "pbf-as-arcgis") { return queryPbfAsGeoJSONOrArcGIS(requestOptions.url, queryOptions); } return request(`${cleanUrl(requestOptions.url)}/query`, queryOptions); } /** * Query a feature service to retrieve all features. See [REST Documentation](https://developers.arcgis.com/rest/services-reference/query-feature-service-layer-.htm) for more information. * * ```js * import { queryAllFeatures } from '@esri/arcgis-rest-feature-service'; * * queryAllFeatures({ * url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3", * where: "STATE_NAME = 'Alaska'" * }) * .then(result) * ``` * * @param requestOptions - Options for the request * @returns A Promise that will resolve with the query response. */ export async function queryAllFeatures(requestOptions) { var _a, _b, _c, _d; let firstResponse = true; let offset = 0; let hasMore = true; let allFeaturesResponse = null; const userRecordCount = requestOptions.resultRecordCount || ((_a = requestOptions.params) === null || _a === void 0 ? void 0 : _a.resultRecordCount); // Throw error if user requests 100,000 or more features if (userRecordCount && userRecordCount >= 100000) { throw new ArcGISRequestError("resultRecordCount must be less than 100,000.", 400, null, requestOptions.url, requestOptions); } let recordCountToUse; if (userRecordCount) { // Use user-provided recordCount directly recordCountToUse = userRecordCount; } else { // retrieve the maxRecordCount for the service only if user did not provide resultRecordCount const pageSizeResponse = await request(requestOptions.url, { httpMethod: "GET", authentication: requestOptions.authentication }); // default the pageSize to 2000 if it is not provided recordCountToUse = pageSizeResponse.maxRecordCount || 2000; } while (hasMore) { const pagedOptions = Object.assign(Object.assign({}, requestOptions), { params: Object.assign(Object.assign({ where: "1=1", outFields: "*" }, (requestOptions.params || {})), { resultOffset: offset, resultRecordCount: recordCountToUse }) }); const queryOptions = appendCustomParams(pagedOptions, [ "where", "objectIds", "relationParam", "time", "distance", "units", "outFields", "geometry", "geometryType", "spatialRel", "returnGeometry", "maxAllowableOffset", "geometryPrecision", "inSR", "outSR", "gdbVersion", "orderByFields", "groupByFieldsForStatistics", "outStatistics", "returnZ", "returnM", "multipatchOption", "resultOffset", "resultRecordCount", "maxRecordCountFactor", "quantizationParameters", "resultType", "historicMoment", "returnTrueCurves", "sqlFormat", "f" ], { httpMethod: "GET", params: Object.assign({ where: "1=1", outFields: "*", returnExceededLimitFeatures: true }, pagedOptions.params) }); let response; if (((_b = queryOptions.params) === null || _b === void 0 ? void 0 : _b.f) === "pbf-as-geojson" || ((_c = queryOptions.params) === null || _c === void 0 ? void 0 : _c.f) === "pbf-as-arcgis") { response = (await queryPbfAsGeoJSONOrArcGIS(requestOptions.url, queryOptions)); } else { response = await request(`${cleanUrl(requestOptions.url)}/query`, queryOptions); } // save the first response structure if (!allFeaturesResponse) { allFeaturesResponse = Object.assign({}, response); } else { // append features of subsequent requests allFeaturesResponse.features = allFeaturesResponse.features.concat(response.features); } const returnedCount = response.features.length; // Use the returned feature count as the page size for subsequent requests if user record count exceeds service limits if (firstResponse) { firstResponse = false; if (returnedCount > 0 && returnedCount < recordCountToUse) { recordCountToUse = returnedCount; } } const exceededTransferLimit = // ArcGIS JSON | pbf-as-arcgis: exceededTransferLimit is on the response object response.exceededTransferLimit || ( // GeoJSON | pbf-as-geojson: exceededTransferLimit is on properties in the response object (_d = response.properties) === null || _d === void 0 ? void 0 : _d.exceededTransferLimit); // check if there are more features if (returnedCount < recordCountToUse || !exceededTransferLimit) { hasMore = false; } else { offset += recordCountToUse; } } return allFeaturesResponse; } //# sourceMappingURL=query.js.map