/// <summary>
        /// Executes a query on the specified layer and returns a featureset result.
        /// </summary>
        /// <param name="layerId">The ID of the layer to query.</param>
        /// <param name="where">The where clause used to query. Any legal SQL where clause operating on the fields in the layer is allowed.</param>
        /// <param name="geometry">The geometry to apply as the spatial filter.</param>
        /// <param name="spatialRel">The spatial relationship to be applied on the input geometry while performing the query.</param>
        /// <param name="outSR">The spatial reference of the returned geometry.</param>
        /// <param name="outFields">The list of fields to be included in the returned result set.</param>
        /// <param name="objectIds">The object IDs of this layer/table to be queried.</param>
        /// <param name="orderByFields">The fields by which to order the results.</param>
        /// <param name="returnGeometry">If set to <c>true</c> the result includes the geometry associated with each feature returned.</param>
        /// <param name="returnDistinctValues">If set to <c>true</c> it returns distinct values based on the fields specified in outFields.</param>
        /// <returns></returns>
        public FeatureSet Query(int layerId,
                                string where,
                                Geometry geometry = null,
                                esriSpatialRelationship spatialRel = esriSpatialRelationship.esriSpatialRelIntersects,
                                SpatialReference outSR             = null,
                                string outFields          = "*",
                                int[] objectIds           = null,
                                string orderByFields      = "",
                                bool returnGeometry       = true,
                                bool returnDistinctValues = false)
        {
            LayerTableBase layer = this.LayerInfos.Single(x => x.ID == layerId);

            if (layer == null)
            {
                Throw.ArgumentOutOfRange("layerId");
            }
            IDictionary <string, object> inputs = new Dictionary <string, object>()
            {
                { "where", where },
                { "objectIds", objectIds != null?string.Join(",", objectIds) : null },
                { "returnGeometry", returnGeometry },
                { "outFields", outFields },
                { "orderByFields", orderByFields },
                { "returnDistinctValues", returnDistinctValues }
            };

            if (outSR != null && outSR.WKID != WKID.NotSpecified)
            {
                inputs.Add("outSR", outSR);
            }
            if (geometry != null)
            {
                try
                {
                    ValidateGeometry(geometry);
                }
                catch (ArgumentNullException ex)
                {
                    Throw.InvalidOperation("", x => new InvalidOperationException("Geometry object is invalid.", ex));
                }
                inputs.Add("geometry", geometry);
                inputs.Add("geometryType", geometry.GeometryType);
                inputs.Add("spatialRel", spatialRel);
                inputs.Add("inSR", geometry.SpatialReference);
            }

            Uri endpoint = GetUrl("query", inputs, layer);

            return(Geocrest.Model.RestHelper.HydrateObject <FeatureSet>(endpoint.ToString()));
        }
        /// <summary>
        /// Executes a query on the specified layer and returns the count of features that match.
        /// </summary>
        /// <param name="layerId">The ID of the layer to query.</param>
        /// <param name="where">The where clause used to query. Any legal SQL where clause operating on the fields in the layer is allowed.</param>
        /// <param name="geometry">The geometry to apply as the spatial filter.</param>
        /// <param name="spatialRel">The spatial relationship to be applied on the input geometry while performing the query.</param>
        /// <param name="outSR">The spatial reference of the returned geometry.</param>
        /// <returns>
        /// The count of features that match the query.
        /// </returns>
        public int QueryForCount(
            int layerId,
            string where,
            Geometry geometry = null,
            esriSpatialRelationship spatialRel = esriSpatialRelationship.esriSpatialRelIntersects,
            SpatialReference outSR             = null)
        {
            LayerTableBase layer = this.LayerInfos.Single(x => x.ID == layerId);

            if (layer == null)
            {
                Throw.ArgumentOutOfRange("layerId");
            }
            IDictionary <string, object> inputs = new Dictionary <string, object>()
            {
                { "where", where },
                { "returnGeometry", false },
                { "returnCountOnly", true }
            };

            if (outSR != null && outSR.WKID != WKID.NotSpecified)
            {
                inputs.Add("outSR", outSR);
            }
            if (geometry != null)
            {
                try
                {
                    ValidateGeometry(geometry);
                }
                catch (ArgumentNullException ex)
                {
                    Throw.InvalidOperation("", x => new InvalidOperationException("Geometry object is invalid.", ex));
                }
                inputs.Add("geometry", geometry);
                inputs.Add("geometryType", geometry.GeometryType);
                inputs.Add("spatialRel", spatialRel);
                inputs.Add("inSR", geometry.SpatialReference);
            }
            Uri endpoint = GetUrl("query", inputs, layer);
            var response = Geocrest.Model.RestHelper.HydrateObject <FeatureSetQuery>(endpoint.ToString());

            return(response.Count);
        }
        /// <summary>
        /// This operation adds, updates, and deletes features to the specified feature layer.
        /// </summary>
        /// <param name="layerId">The ID of the layer to query.</param>
        /// <param name="adds">The array of features to be added.</param>
        /// <param name="updates">The array of features to be updated. The attributes property of
        /// each feature should include the object ID of the feature.</param>
        /// <param name="deletes">The array of object IDs to be deleted.</param>
        /// <returns></returns>
        public ApplyEditsResult ApplyEdits(int layerId, Feature[] adds = null, Feature[] updates = null, long[] deletes = null)
        {
            LayerTableBase layer = this.LayerInfos.Single(x => x.ID == layerId);

            if (layer == null)
            {
                Throw.ArgumentOutOfRange("layerId");
            }
            IDictionary <string, object> inputs = new Dictionary <string, object>()
            {
            };
            Uri endpoint = GetUrl("applyEdits", inputs, layer);
            var nvc      = new NameValueCollection();

            nvc.Add("adds", JsonConvert.SerializeObject(adds) ?? "[]");
            nvc.Add("updates", JsonConvert.SerializeObject(updates) ?? "[]");
            nvc.Add("deletes", JsonConvert.SerializeObject(deletes) ?? "[]");
            return(Geocrest.Model.RestHelper.HydrateObject <ApplyEditsResult>(endpoint.ToString(), nvc));
        }
        /// <summary>
        /// Gets the parameterized URL to submit to the server.
        /// </summary>
        /// <param name="operation">The operation to perform.</param>
        /// <param name="parameters">A collection of input parameters to submit to the server.</param>
        /// <param name="layer">The layer on which to perform the operation.</param>
        /// <returns>
        /// Returns an instance of <see cref="T:System.String" />.
        /// </returns>
        protected internal Uri GetUrl(string operation, IDictionary <string, object> parameters, LayerTableBase layer)
        {
            Throw.IfArgumentNull(layer, "layer");
            var endpoint = GetUrl(operation, parameters);
            var uri      = new UriBuilder(endpoint);

            uri.Path = uri.Path.Replace("/" + operation, string.Format("/{0}/{1}", layer.ID, operation));
            return(uri.Uri);
        }