diff --git a/demos/feature-service-browser/README.md b/demos/feature-service-browser/README.md new file mode 100644 index 0000000000..12dccc614a --- /dev/null +++ b/demos/feature-service-browser/README.md @@ -0,0 +1,6 @@ +# Running this demo + +1. Make sure you run `npm run bootstrap` in the root folder to setup the dependencies +1. `npm start` +1. Visit http://localhost:8080 +1. Enter a search term and click "Search" to see results diff --git a/demos/feature-service-browser/index.html b/demos/feature-service-browser/index.html new file mode 100644 index 0000000000..6a761197bc --- /dev/null +++ b/demos/feature-service-browser/index.html @@ -0,0 +1,122 @@ + + + + + + + +
+
+
+
+

query features!

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+ + + + + + + + + + + +

Try 'elm' or 'oak' for Type. Try 'fair' or 'good' for Condition.

+

+ +
+
+
+ + + + + + + + diff --git a/demos/feature-service-browser/package.json b/demos/feature-service-browser/package.json new file mode 100644 index 0000000000..9bca889643 --- /dev/null +++ b/demos/feature-service-browser/package.json @@ -0,0 +1,18 @@ +{ + "name": "feature-service-browser", + "version": "1.0.3", + "private": true, + "description": "Vanilla JavaScript demo of @esri/arcgis-rest-feature-service", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@esri/arcgis-rest-request": "^1.0.3", + "@esri/arcgis-rest-feature-service": "^1.0.3" + }, + "devDependencies": { + "http-server": "*" + }, + "scripts": { + "start": "http-server ." + } +} diff --git a/packages/arcgis-rest-feature-service/package.json b/packages/arcgis-rest-feature-service/package.json index 9d81027e0f..4ef4e2b854 100644 --- a/packages/arcgis-rest-feature-service/package.json +++ b/packages/arcgis-rest-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@esri/arcgis-rest-feature-service", - "version": "1.0.2", + "version": "1.0.3", "description": "Feature service helpers for @esri/arcgis-rest-request", "main": "dist/node/index.js", "browser": "dist/umd/arcgis-rest-feature-service.umd.js", diff --git a/packages/arcgis-rest-feature-service/src/features.ts b/packages/arcgis-rest-feature-service/src/features.ts index b3c170cb65..9940dab26d 100644 --- a/packages/arcgis-rest-feature-service/src/features.ts +++ b/packages/arcgis-rest-feature-service/src/features.ts @@ -1,6 +1,12 @@ /* Copyright (c) 2017 Environmental Systems Research Institute, Inc. * Apache-2.0 */ -import { IFeature } from "@esri/arcgis-rest-common-types"; +import { + esriGeometryType, + IFeature, + IField, + IGeometry, + ISpatialReference +} from "@esri/arcgis-rest-common-types"; import { request, IRequestOptions } from "@esri/arcgis-rest-request"; /** @@ -15,7 +21,101 @@ export interface IFeatureRequestOptions extends IRequestOptions { } /** - * Get an feature by id + * @param statisticType - statistical operation to perform (count, sum, min, max, avg, stddev, var) + * @param onStatisticField - field on which to perform the statistical operation + * @param outStatisticFieldName - a field name for the returned statistic field. If outStatisticFieldName is empty or missing, the server will assign one. A valid field name can only contain alphanumeric characters and an underscore. If the outStatisticFieldName is a reserved keyword of the underlying DBMS, the operation can fail. Try specifying an alternative outStatisticFieldName. + */ +export interface IStatisticDefinition { + statisticType: "count" | "sum" | "min" | "max" | "avg" | "stddev" | "var"; + onStatisticField: string; + outStatisticFieldName: string; +} + +/** + * feature query parameters + * + * See https://developers.arcgis.com/rest/services-reference/query-feature-service-layer-.htm + */ +export interface IQueryFeaturesParams { + // TODO: are _any_ of these required? + where?: string; + objectIds?: [number]; + geometry?: IGeometry; + geometryType?: esriGeometryType; + // NOTE: either WKID or ISpatialReference + inSR?: string | ISpatialReference; + spatialRel?: + | "esriSpatialRelIntersects" + | "esriSpatialRelContains" + | "esriSpatialRelCrosses" + | "esriSpatialRelEnvelopeIntersects" + | "esriSpatialRelIndexIntersects" + | "esriSpatialRelOverlaps" + | "esriSpatialRelTouches" + | "esriSpatialRelWithin"; + relationParam?: string; + // NOTE: either time=1199145600000 or time=1199145600000, 1230768000000 + time?: Date | [Date]; + distance?: number; + units?: + | "esriSRUnit_Meter" + | "esriSRUnit_StatuteMile" + | "esriSRUnit_Foot" + | "esriSRUnit_Kilometer" + | "esriSRUnit_NauticalMile" + | "esriSRUnit_USNauticalMile"; + outFields?: "*" | [string]; + returnGeometry?: boolean; + maxAllowableOffset?: number; + geometryPrecision?: number; + // NOTE: either WKID or ISpatialReference + outSR?: string | ISpatialReference; + gdbVersion?: string; + returnDistinctValues?: boolean; + returnIdsOnly?: boolean; + returnCountOnly?: boolean; + returnExtentOnly?: boolean; + orderByFields?: string; + groupByFieldsForStatistics?: string; + outStatistics?: [IStatisticDefinition]; + returnZ?: boolean; + returnM?: boolean; + multipatchOption?: "xyFootprint"; + resultOffset?: number; + resultRecordCount?: number; + // TODO: IQuantizationParameters? + quantizationParameters?: any; + returnCentroid?: boolean; + resultType?: "none" | "standard" | "tile"; + // TODO: is Date the right type for epoch time in milliseconds? + historicMoment?: Date; + returnTrueCurves?: false; + sqlFormat?: "none" | "standard" | "native"; + returnExceededLimitFeatures?: boolean; +} + +/** + * feature query request options + * + * @param url - layer service url + * @param params - query parameters to be sent to the feature service + */ +export interface IQueryFeaturesRequestOptions extends IRequestOptions { + url: string; + params?: IQueryFeaturesParams; +} + +export interface IQueryFeaturesResponse { + objectIdFieldName: string; + globalIdFieldName: string; + geometryType: esriGeometryType; + spatialReference: ISpatialReference; + fields: [IField]; + features: [IFeature]; +} + +/** + * Get a feature by id * * @param requestOptions - Options for the request * @returns A Promise that will resolve with the feature. @@ -32,3 +132,31 @@ export function getFeature( }; return request(url, options).then((response: any) => response.feature); } + +/** + * Query features + * + * @param requestOptions - Options for the request + * @returns A Promise that will resolve with the query response. + */ +export function queryFeatures( + requestOptions: IQueryFeaturesRequestOptions +): Promise { + // set default query parameters + // and default to a GET request + const options: IQueryFeaturesRequestOptions = { + ...{ + params: {}, + httpMethod: "GET" + }, + ...requestOptions + }; + if (!options.params.where) { + options.params.where = "1=1"; + } + if (!options.params.outFields) { + options.params.outFields = "*"; + } + // TODO: do we need to serialize any of the array/object params? + return request(`${requestOptions.url}/query`, options); +} diff --git a/packages/arcgis-rest-feature-service/test/features.test.ts b/packages/arcgis-rest-feature-service/test/features.test.ts index 1a231e5de5..b97ce87bbe 100644 --- a/packages/arcgis-rest-feature-service/test/features.test.ts +++ b/packages/arcgis-rest-feature-service/test/features.test.ts @@ -1,8 +1,8 @@ -import { getFeature } from "../src/index"; +import { getFeature, queryFeatures } from "../src/index"; import * as fetchMock from "fetch-mock"; -import { featureResponse } from "./mocks/feature"; +import { featureResponse, queryResponse } from "./mocks/feature"; describe("feature", () => { afterEach(fetchMock.restore); @@ -23,4 +23,20 @@ describe("feature", () => { done(); }); }); + + it("should supply default query parameters", done => { + const params = { + url: + "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Landscape_Trees/FeatureServer/0" + }; + fetchMock.once("*", queryResponse); + queryFeatures(params).then(response => { + expect(fetchMock.called()).toBeTruthy(); + const [url, options]: [string, RequestInit] = fetchMock.lastCall("*"); + expect(url).toEqual(`${params.url}/query?f=json&where=1%3D1&outFields=*`); + expect(options.method).toBe("GET"); + // expect(response.attributes.FID).toEqual(42); + done(); + }); + }); }); diff --git a/packages/arcgis-rest-feature-service/test/mocks/feature.ts b/packages/arcgis-rest-feature-service/test/mocks/feature.ts index 3bdfb43266..401f5e2242 100644 --- a/packages/arcgis-rest-feature-service/test/mocks/feature.ts +++ b/packages/arcgis-rest-feature-service/test/mocks/feature.ts @@ -11,3 +11,136 @@ export const featureResponse = { } } }; + +export const queryResponse = { + objectIdFieldName: "FID", + globalIdFieldName: "", + geometryType: "esriGeometryPoint", + spatialReference: { + wkid: 102100, + latestWkid: 3857 + }, + // fields: [], + features: [ + { + attributes: { + FID: 1, + Tree_ID: 102, + Collected: 1349395200000, + Crew: "Linden+ Forrest+ Johnny", + Status: "P", + Spp_Code: "ULPU", + Land_Use: "I", + Ht_DBH_ft: 4.5, + DBH1: 41, + DBH2: 0, + DBH3: 0, + DBH4: 0, + DBH5: 0, + DBH6: " ", + Height: 74, + Live_Top: 74, + Crown_Base: 21, + Width_NS: 75, + Width_EW: 40, + Cn_Missing: 10, + Cn_DieBack: 15, + CLE: 2, + Tree_Site: "S", + Tree_Age: " ", + Notes: " ", + Cmn_Name: "Siberian elm", + Sci_Name: "Ulmus pumila", + GroundArea: 2596, + Condition: "Fair", + Leaf_Area: 10295, + Leaf_Bmass: 144, + LAI: 3.96, + C_Storage: 6771, + C_Seq: 95, + S_Value: "3,079.00", + Street: "YES", + Native: "NO", + CO_Rmvd: 5.06, + O3_Rmvd: 470.97, + NO2_Rmvd: 39.24, + PM10_Rmvd: 343.45, + SO2_Rmvd: 27.64, + PM2p5_Rmvd: 47.84, + CO_RVlu: 0.01, + O3_Rvlu: 0.42, + NO2_Rvlu: 0, + PM10_Rvlu: 3.73, + SO2_Rvlu: 0, + PM2p5_RVlu: 1.99, + Isoprene_E: 2.7, + Monoterp_E: 13.2, + Vocs_E: 15.9, + Dedication: " ", + Longitude: -82.441189, + Latitude: 35.610441, + Crown_Height: 53 + } + }, + { + attributes: { + FID: 2, + Tree_ID: 103, + Collected: 1349395200000, + Crew: "Linden+ Forrest+ Johnny", + Status: "P", + Spp_Code: "ULPU", + Land_Use: "I", + Ht_DBH_ft: 4.5, + DBH1: 55, + DBH2: 0, + DBH3: 0, + DBH4: 0, + DBH5: 0, + DBH6: " ", + Height: 70, + Live_Top: 70, + Crown_Base: 21, + Width_NS: 90, + Width_EW: 71, + Cn_Missing: 5, + Cn_DieBack: 15, + CLE: 3, + Tree_Site: "S", + Tree_Age: " ", + Notes: " ", + Cmn_Name: "Siberian elm", + Sci_Name: "Ulmus pumila", + GroundArea: 5089, + Condition: "Fair", + Leaf_Area: 17078, + Leaf_Bmass: 238, + LAI: 3.36, + C_Storage: 13228, + C_Seq: 22, + S_Value: "4,187.00", + Street: "YES", + Native: "NO", + CO_Rmvd: 8.4, + O3_Rmvd: 781.35, + NO2_Rmvd: 65.1, + PM10_Rmvd: 600.8, + SO2_Rmvd: 45.86, + PM2p5_Rmvd: 83.68, + CO_RVlu: 0.01, + O3_Rvlu: 0.69, + NO2_Rvlu: 0.01, + PM10_Rvlu: 6.53, + SO2_Rvlu: 0, + PM2p5_RVlu: 3.48, + Isoprene_E: 4.47, + Monoterp_E: 21.9, + Vocs_E: 26.38, + Dedication: " ", + Longitude: -82.441107, + Latitude: 35.610472, + Crown_Height: 49 + } + } + ] +};