public string GetCachedTileFilePathName(TileInfo tileInfo) { return(Path.Combine(GetDirectoryName(tileInfo), string.Format(CultureInfo.InvariantCulture, "{0}.{1}", tileInfo.TileY, format.ToString()))); }
/// <summary> /// Creates and Serves vector tiles /// </summary> /// <param name="boundVariables"></param> /// <param name="operationInput"></param> /// <param name="outputFormat"></param> /// <param name="requestProperties"></param> /// <param name="responseProperties"></param> /// <returns></returns> private byte[] VectorTileHandler(NameValueCollection boundVariables, JsonObject operationInput, string outputFormat, string requestProperties, out string responseProperties) { //responseProperties = null; responseProperties = null; //"Content-Type:application/json"; ESRI.ArcGIS.SOESupport.AutoTimer timer = new AutoTimer(); const string methodName = "VectorTileHandler"; try { long? layerIndex; long? zoom; long? row; long? col; string jsonFormatParam; TileFormat jsonFormat = TileFormat.EsriJson; // Defaulting to EsriJson if (!operationInput.TryGetAsLong("l", out layerIndex)) { throw new ArgumentNullException("layer"); } if (!operationInput.TryGetAsLong("z", out zoom)) { throw new ArgumentNullException("zoom"); } if (!operationInput.TryGetAsLong("y", out row)) { throw new ArgumentNullException("row"); } if (!operationInput.TryGetAsLong("x", out col)) { throw new ArgumentNullException("col"); } if (operationInput.TryGetString("jf", out jsonFormatParam)) { if (!string.IsNullOrEmpty(jsonFormatParam)) { jsonFormatParam = jsonFormatParam.ToLower().Trim(); Enum.GetNames(typeof(TileFormat)).ToList().ForEach(n => { if (n.ToLower() == jsonFormatParam) { jsonFormat = (TileFormat)Enum.Parse(typeof(TileFormat), jsonFormatParam, true); } }); } } //System.Diagnostics.Debug.WriteLine(string.Format("l:{0}, r:{1}, c:{2}", zoom, row, col)); // Check to see if the tile exists on disk... // <cache-root>\<layerId>\<zoom>\<row>\<col>.esrijson; //i.e. to be consistent with Esri tile caching structure string tilePath = string.Format(@"{0}\{1}\{2}\{3}\{4}.{5}", _vectorCacheRootDirectory, layerIndex, zoom.Value, row.Value, col.Value, jsonFormat.ToString().ToLower()); if (File.Exists(tilePath)) { // Fetch tile contents from disk _dtsLogger.LogInfo(soe_name, methodName, "Time: " + timer.Elapsed.ToString()); logger.LogMessage(ServerLogger.msgType.infoSimple, methodName, -1, "Time: " + timer.Elapsed.ToString()); return(this.ReadTileFile(tilePath)); } else { // Write new files to disk IMapServer3 mapServer = serverObjectHelper.ServerObject as IMapServer3; if (mapServer == null) { throw new InvalidOperationException("Unable to access the map server."); } // Get the bbox. Returns an envelope in WGS84 (4326). IEnvelope env102100 = TileUtil.GetEnvelopeFromZoomRowCol((int)zoom.Value, (int)row.Value, (int)col.Value); //_dtsLogger.LogInfo(soe_name, methodName, this.GeometryToXml(env4326)); // Convert envelope to polygon b/c QueryData does not support spatialfilter geometry using envelope IPolygon polygon102100 = this.CreatePolygonFromEnvelope(env102100); // Use QueryData and generalize result geometries based on zoom level IQueryResultOptions resultOptions = new QueryResultOptionsClass(); // i.e; IRecordSet to support BOTH json and geojson resultOptions.Format = esriQueryResultFormat.esriQueryResultRecordSetAsObject; IMapTableDescription tableDescription = this.GetTableDescription(mapServer, (int)layerIndex, (int)zoom); // Create spatial filter ISpatialFilter spatialFilter = new SpatialFilterClass(); spatialFilter.Geometry = polygon102100; spatialFilter.GeometryField = "Shape"; spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; //TODO: Subfields should be configurable spatialFilter.SubFields = "*"; // Execute query IQueryResult result = mapServer.QueryData(mapServer.DefaultMapName, tableDescription, spatialFilter, resultOptions); byte[] json = null; // TODO: Support writing tiles for no data // Need a way to do this for GeoJson; parse Esri JSON recordset into GeoJson if (result == null || !this.RecordsetHasFeatures(result.Object as IRecordSet)) { // Write "no data" tile if (jsonFormat == TileFormat.EsriJson) { resultOptions.Format = esriQueryResultFormat.esriQueryResultJsonAsMime; result = mapServer.QueryData(mapServer.DefaultMapName, tableDescription, spatialFilter, resultOptions); json = result.MimeData; } else { json = StrToByteArray(NO_DATA_GEOJSON); } } else { //We have features... IRecordSet features = result.Object as IRecordSet; // Get geometry type for query layer esriGeometryType geometryType = this.GetLayerGeometryType(mapServer.GetServerInfo(mapServer.DefaultMapName).MapLayerInfos, (int)layerIndex); switch (geometryType) { case esriGeometryType.esriGeometryPoint: // Do nothing... already intersected json = ESRI.ArcGIS.SOESupport.Conversion.ToJson(features); break; case esriGeometryType.esriGeometryPolyline: // Polylines must be clipped to envelope IFeatureCursor cursor = null; this.ClipFeaturesToTile(ref cursor, ref features, env102100); json = ESRI.ArcGIS.SOESupport.Conversion.ToJson(features); this.ReleaseComObject(cursor); break; case esriGeometryType.esriGeometryMultipoint: case esriGeometryType.esriGeometryPolygon: // Get IDs of features whose centroid is contained by tile envelope List <int> ids = this.GetIdsOfContainedFeatureCentroids(features, polygon102100); if (ids.Count == 0) { // Write no data tile if (jsonFormat == TileFormat.EsriJson) { // Query to get empty featureset and serialize to disk resultOptions.Format = esriQueryResultFormat.esriQueryResultJsonAsMime; IQueryFilter queryFilter = new QueryFilterClass(); queryFilter.SubFields = "*"; queryFilter.WhereClause = "1=2"; result = mapServer.QueryData(mapServer.DefaultMapName, tableDescription, queryFilter, resultOptions); json = result.MimeData; } else { json = StrToByteArray(NO_DATA_GEOJSON); } } else { // Execute new query for IDs IQueryFilter queryFilter = new QueryFilterClass(); queryFilter.SubFields = "*"; // TODO: Account for sql query syntax based on datasource // FGDB/Shapefile then "OBJECTID"; quotes required // PGDB then [OBJECTID]; brackets required // SDE then OBJECTID; nothing but the fieldname queryFilter.WhereClause = string.Format("\"OBJECTID\" IN({0})", ids.ToDelimitedString <int>(",")); // FGDB result = mapServer.QueryData(mapServer.DefaultMapName, tableDescription, queryFilter, resultOptions); features = result.Object as IRecordSet; // Do some checking here... var featureCount = this.GetRecordsetFeatureCount(features); if (featureCount != ids.Count) { System.Diagnostics.Debug.WriteLine(string.Format("Query Problem: ID search results IS NOT EQUAL to contained IDs count - [{0}:{1}]", featureCount, ids.Count)); System.Diagnostics.Debug.WriteLine("Query: " + queryFilter.WhereClause); } json = ESRI.ArcGIS.SOESupport.Conversion.ToJson(features); } break; default: throw new NotSupportedException(string.Format("Geometry type {0} is not supported by {1}", geometryType.ToString(), soe_name)); } } //store the json to disk this.WriteTileFile(json, tilePath, (int)layerIndex, (int)zoom, (int)row); _dtsLogger.LogInfo(soe_name, methodName, "Time: " + timer.Elapsed.ToString()); logger.LogMessage(ServerLogger.msgType.infoSimple, methodName, -1, "Time: " + timer.Elapsed.ToString()); return(json); } } catch (Exception ex) { // Log the error _dtsLogger.LogError(soe_name, methodName, "n/a", ex); logger.LogMessage(ServerLogger.msgType.error, methodName, 9999, ex.StackTrace); return(StrToByteArray("{}")); } }