async static void subAdd(Feature poiFeat, string dzx, Layer newLayer) { await Task.Run(() => { OSGeo.OGR.Ogr.RegisterAll(); OSGeo.OGR.Driver dr = OSGeo.OGR.Ogr.GetDriverByName("ESRI shapefile"); OSGeo.OGR.DataSource dzxDS = dr.Open(dzx, 0); OSGeo.OGR.Layer dzxLayer = dzxDS.GetLayerByIndex(0); int dzxCount = dzxLayer.GetFeatureCount(0); OSGeo.OGR.Geometry poiGeom = poiFeat.GetGeometryRef(); for (int i = 0; i < dzxLayer.GetFeatureCount(0); i++) { OSGeo.OGR.Feature dzxFeat = dzxLayer.GetFeature(i); OSGeo.OGR.Geometry dzxGeom = dzxFeat.GetGeometryRef(); if (poiGeom.Within(dzxGeom)) { newLayer.CreateFeature(dzxFeat); } dzxGeom.Dispose(); dzxFeat.Dispose(); } poiFeat.Dispose(); dzxDS.Dispose(); tickTime++; }); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { ///GDAL setup RESTful.GdalConfiguration.ConfigureOgr(); ///Working with data trees allows us to only call the osr coordinate transformation once, which seems to be expensive GH_Structure <GH_Point> sourcePoints = new GH_Structure <GH_Point>(); DA.GetDataTree(0, out sourcePoints); GH_Structure <GH_Point> destPoints = new GH_Structure <GH_Point>(); string sourceString = string.Empty; DA.GetData(1, ref sourceString); OSGeo.OSR.SpatialReference sourceSRS = new OSGeo.OSR.SpatialReference(""); sourceSRS.SetFromUserInput(sourceString); if (sourceSRS.Validate() == 1) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid Source SRS."); return; } string destString = string.Empty; DA.GetData(2, ref destString); OSGeo.OSR.SpatialReference destSRS = new OSGeo.OSR.SpatialReference(""); destSRS.SetFromUserInput(destString); if (destSRS.Validate() == 1) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid Destination SRS."); return; } OSGeo.OSR.CoordinateTransformation trans = new OSGeo.OSR.CoordinateTransformation(sourceSRS, destSRS); foreach (var path in sourcePoints.Paths) { List <GH_Point> branchPts = (List <GH_Point>)sourcePoints.get_Branch(path); foreach (var sp in branchPts) { OSGeo.OGR.Geometry destOgrPoint = new OSGeo.OGR.Geometry(wkbGeometryType.wkbPoint); destOgrPoint.AddPoint(sp.Value.X, sp.Value.Y, sp.Value.Z); destOgrPoint.AssignSpatialReference(sourceSRS); destOgrPoint.Transform(trans); Point3d destPoint = new Point3d(destOgrPoint.GetX(0), destOgrPoint.GetY(0), destOgrPoint.GetZ(0)); destPoints.Append(new GH_Point(destPoint), path); destOgrPoint.Dispose(); } } DA.SetDataTree(0, destPoints); }
/// <summary> /// 子线程提取轮廓线ID /// </summary> /// <param name="pdFeat"></param> /// <param name="dzx"></param> async static void select(Feature pdFeat, string dzx) { await Task.Run(() => { double afterCha = -1; int yesID = -1; OSGeo.OGR.Ogr.RegisterAll(); OSGeo.OGR.Driver dr = OSGeo.OGR.Ogr.GetDriverByName("ESRI shapefile"); OSGeo.OGR.DataSource dzDS = dr.Open(dzx, 0); OSGeo.OGR.Layer dzxLayer = dzDS.GetLayerByIndex(0); OSGeo.OGR.Geometry pdGeom = pdFeat.GetGeometryRef(); int dzCount = dzxLayer.GetFeatureCount(0); for (int dzi = 0; dzi < dzCount; dzi++) { OSGeo.OGR.Feature dzFeat = dzxLayer.GetFeature(dzi); OSGeo.OGR.Geometry dzGeom = dzFeat.GetGeometryRef(); if (pdGeom.Intersect(dzGeom)) { double cha = Math.Abs(dzGeom.GetArea() - pdGeom.GetArea()); if (afterCha == -1) { afterCha = cha; yesID = dzi; } else if (cha < afterCha) { afterCha = cha; yesID = dzi; } } dzGeom.Dispose(); dzFeat.Dispose(); } if (yesID != -1) { minIdG.Add(yesID); } dzDS.Dispose(); dzxLayer.Dispose(); pdGeom.Dispose(); pdFeat.Dispose(); tickTime++; }); }
/************************************* 判断两个Featuer是否重复 *************************************************/ /// <summary> /// 判断两个Featuer是否重复,ori 当前Feat,next 目标Feat /// </summary> /// <param name="ori"></param> /// <param name="next"></param> /// <returns></returns> public static bool isSame(OSGeo.OGR.Feature ori, OSGeo.OGR.Feature next, double fanWei = 0.1) { OSGeo.OGR.Ogr.RegisterAll(); OSGeo.OGR.Geometry oriGeom = ori.GetGeometryRef(); OSGeo.OGR.Envelope oriEnve = new OSGeo.OGR.Envelope(); oriGeom.GetEnvelope(oriEnve); OSGeo.OGR.Geometry nextGeom = next.GetGeometryRef(); OSGeo.OGR.Envelope nextEnve = new OSGeo.OGR.Envelope(); nextGeom.GetEnvelope(nextEnve); double oriArea = oriGeom.GetArea(); double nextArea = nextGeom.GetArea(); bool res = Math.Abs(oriEnve.MaxX - nextEnve.MaxX) < fanWei && //外接矩形差 Math.Abs(oriEnve.MaxY - nextEnve.MaxY) < fanWei && Math.Abs(oriEnve.MinX - nextEnve.MinX) < fanWei && Math.Abs(oriEnve.MinY - nextEnve.MinY) < fanWei; //面积? && Math.Abs(oriArea - nextArea) < 0.1; oriGeom.Dispose(); oriEnve.Dispose(); nextGeom.Dispose(); nextEnve.Dispose(); return(res); }
protected override void SolveInstance(IGH_DataAccess DA) { ///Gather GHA inputs List <Curve> boundary = new List <Curve>(); DA.GetDataList <Curve>("Boundary", boundary); string shpFilePath = string.Empty; DA.GetData <string>("Vector Data Location", ref shpFilePath); bool cropIt = true; DA.GetData <Boolean>("Crop file", ref cropIt); string userSRStext = "WGS84"; DA.GetData <string>(2, ref userSRStext); ///GDAL setup ///Some preliminary testing has been done to read SHP, GeoJSON, OSM, KML, MVT, GML and GDB ///It can be spotty with KML, MVT and GML and doesn't throw informative errors. Likely has to do with getting a valid CRS and ///TODO: resolve errors with reading KML, MVT, GML. DataSource dataSource = CreateDataSource(shpFilePath); List <Layer> layerSet = GetLayers(dataSource); ///Declare trees GH_Structure <GH_Rectangle> recs = new GH_Structure <GH_Rectangle>(); GH_Structure <GH_String> spatialReferences = new GH_Structure <GH_String>(); GH_Structure <GH_String> fnames = new GH_Structure <GH_String>(); GH_Structure <GH_String> fset = new GH_Structure <GH_String>(); GH_Structure <GH_Point> gset = new GH_Structure <GH_Point>(); GH_Structure <GH_Point> gsetUser = new GH_Structure <GH_Point>(); GH_Structure <GH_Rectangle> recsUser = new GH_Structure <GH_Rectangle>(); GH_Structure <GH_String> gtype = new GH_Structure <GH_String>(); GH_Structure <IGH_GeometricGoo> gGoo = new GH_Structure <IGH_GeometricGoo>(); ///Loop through each layer. Layers usually occur in Geodatabase GDB format. SHP usually has only one layer. for (int iLayer = 0; iLayer < dataSource.GetLayerCount(); iLayer++) { OSGeo.OGR.Layer layer = dataSource.GetLayerByIndex(iLayer); if (layer == null) { Console.WriteLine($"Couldn't fetch advertised layer {iLayer}"); System.Environment.Exit(-1); } long count = layer.GetFeatureCount(1); int featureCount = System.Convert.ToInt32(count); AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"Layer #{iLayer} {layer.GetName()} has {featureCount} features"); ///Get the spatial reference of the input vector file and set to WGS84 if not known /// OSGeo.OSR.SpatialReference sourceSRS = new SpatialReference(Osr.SRS_WKT_WGS84); string spatialReference = GetSpatialReference(layer, iLayer, dataSource, sourceSRS); spatialReferences.Append(new GH_String(spatialReference), new GH_Path(iLayer)); ///Set transform from input spatial reference to Rhino spatial reference ///TODO: look into adding a step for transforming to CRS set in SetCRS OSGeo.OSR.SpatialReference rhinoSRS = new OSGeo.OSR.SpatialReference(""); rhinoSRS.SetWellKnownGeogCS("WGS84"); ///TODO: verify the userSRS is valid ///TODO: use this as override of global SetSRS OSGeo.OSR.SpatialReference userSRS = new OSGeo.OSR.SpatialReference(""); userSRS.SetFromUserInput(userSRStext); ///These transforms move and scale in order to go from userSRS to XYZ and vice versa Transform userSRSToModelTransform = Heron.Convert.GetUserSRSToModelTransform(userSRS); Transform modelToUserSRSTransform = Heron.Convert.GetModelToUserSRSTransform(userSRS); Transform sourceToModelSRSTransform = Heron.Convert.GetUserSRSToModelTransform(sourceSRS); Transform modelToSourceSRSTransform = Heron.Convert.GetModelToUserSRSTransform(sourceSRS); ///Get OGR envelope of the data in the layer in the sourceSRS OSGeo.OGR.Envelope ext = new OSGeo.OGR.Envelope(); layer.GetExtent(ext, 1); OSGeo.OGR.Geometry extMinSourceOgr = new OSGeo.OGR.Geometry(wkbGeometryType.wkbPoint); extMinSourceOgr.AddPoint(ext.MinX, ext.MinY, 0.0); extMinSourceOgr.AssignSpatialReference(sourceSRS); OSGeo.OGR.Geometry extMaxSourceOgr = new OSGeo.OGR.Geometry(wkbGeometryType.wkbPoint); extMaxSourceOgr.AddPoint(ext.MaxX, ext.MaxY, 0.0); extMaxSourceOgr.AssignSpatialReference(sourceSRS); ///Get extents in Rhino SRS Point3d extPTmin = Heron.Convert.OgrPointToPoint3d(extMinSourceOgr, sourceToModelSRSTransform); Point3d extPTmax = Heron.Convert.OgrPointToPoint3d(extMaxSourceOgr, sourceToModelSRSTransform); Rectangle3d rec = new Rectangle3d(Plane.WorldXY, extPTmin, extPTmax); recs.Append(new GH_Rectangle(rec), new GH_Path(iLayer)); ///Get extents in userSRS ///Can give odd results if crosses 180 longitude extMinSourceOgr.TransformTo(userSRS); extMaxSourceOgr.TransformTo(userSRS); Point3d extPTminUser = Heron.Convert.OgrPointToPoint3d(extMinSourceOgr, userSRSToModelTransform); Point3d extPTmaxUser = Heron.Convert.OgrPointToPoint3d(extMaxSourceOgr, userSRSToModelTransform); Rectangle3d recUser = new Rectangle3d(Plane.WorldXY, extPTminUser, extPTmaxUser); recsUser.Append(new GH_Rectangle(recUser), new GH_Path(iLayer)); if (boundary.Count == 0 && cropIt == true) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Define a boundary or set cropIt to False"); } else if (boundary.Count == 0 && cropIt == false) { AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Clipping boundary has not been defined. File extents will be used instead"); boundary.Add(rec.ToNurbsCurve()); } ///Loop through input boundaries for (int i = 0; i < boundary.Count; i++) { OSGeo.OGR.FeatureDefn def = layer.GetLayerDefn(); ///Get the field names List <string> fieldnames = new List <string>(); for (int iAttr = 0; iAttr < def.GetFieldCount(); iAttr++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iAttr); fnames.Append(new GH_String(fdef.GetNameRef()), new GH_Path(i, iLayer)); } ///Check if boundary is contained in extent if (!rec.IsValid || ((rec.Height == 0) && (rec.Width == 0))) { ///Get field data if even if no geometry is present in the layer AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "One or more vector datasource bounds are not valid."); OSGeo.OGR.Feature feat; int m = 0; while ((feat = layer.GetNextFeature()) != null) { ///Loop through field values for (int iField = 0; iField < feat.GetFieldCount(); iField++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iField); fset.Append(new GH_String(feat.GetFieldAsString(iField)), new GH_Path(i, iLayer, m)); fdef.Dispose(); } m++; feat.Dispose(); } } else if (boundary[i] == null) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Clipping boundary " + i + " not set."); } else if (!boundary[i].IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Clipping boundary " + i + " is not valid."); } else if (rec.IsValid && Curve.PlanarClosedCurveRelationship(rec.ToNurbsCurve(), boundary[i], Plane.WorldXY, Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance) == RegionContainment.Disjoint) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "One or more clipping boundaries may be outside the bounds of the vector datasource."); } else { ///Create bounding box for clipping geometry Point3d min = boundary[i].GetBoundingBox(true).Min; Point3d max = boundary[i].GetBoundingBox(true).Max; min.Transform(modelToSourceSRSTransform); max.Transform(modelToSourceSRSTransform); double[] minpT = new double[3]; double[] maxpT = new double[3]; minpT[0] = min.X; minpT[1] = min.Y; minpT[2] = min.Z; maxpT[0] = max.X; maxpT[1] = max.Y; maxpT[2] = max.Z; OSGeo.OGR.Geometry ebbox = OSGeo.OGR.Geometry.CreateFromWkt("POLYGON((" + minpT[0] + " " + minpT[1] + ", " + minpT[0] + " " + maxpT[1] + ", " + maxpT[0] + " " + maxpT[1] + ", " + maxpT[0] + " " + minpT[1] + ", " + minpT[0] + " " + minpT[1] + "))"); ///Create bounding box for clipping geometry ///Not working on MVT type files //boundary[i].Transform(modelToSourceSRSTransform); //OSGeo.OGR.Geometry ebbox = Heron.Convert.CurveToOgrPolygon(boundary[i]); ///Clip Shapefile ///http://pcjericks.github.io/py-gdalogr-cookbook/vector_layers.html OSGeo.OGR.Layer clipped_layer = layer; if (cropIt) { clipped_layer.SetSpatialFilter(ebbox); } ///Loop through geometry OSGeo.OGR.Feature feat; def = clipped_layer.GetLayerDefn(); int m = 0; while ((feat = clipped_layer.GetNextFeature()) != null) { OSGeo.OGR.Geometry geom = feat.GetGeometryRef(); OSGeo.OGR.Geometry sub_geom; OSGeo.OGR.Geometry geomUser = feat.GetGeometryRef().Clone(); OSGeo.OGR.Geometry sub_geomUser; ///reproject geometry to WGS84 and userSRS ///TODO: look into using the SetCRS global variable here if (geom.GetSpatialReference() == null) { geom.AssignSpatialReference(sourceSRS); } if (geomUser.GetSpatialReference() == null) { geomUser.AssignSpatialReference(sourceSRS); } geom.TransformTo(rhinoSRS); geomUser.TransformTo(userSRS); gtype.Append(new GH_String(geom.GetGeometryName()), new GH_Path(i, iLayer, m)); if (feat.GetGeometryRef() != null) { ///Convert GDAL geometries to IGH_GeometricGoo gGoo.AppendRange(Heron.Convert.OgrGeomToGHGoo(geomUser, userSRSToModelTransform), new GH_Path(i, iLayer, m)); /// Get Feature Values if (fset.PathExists(new GH_Path(i, iLayer, m))) { fset.get_Branch(new GH_Path(i, iLayer, m)).Clear(); } for (int iField = 0; iField < feat.GetFieldCount(); iField++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iField); if (feat.IsFieldSet(iField)) { fset.Append(new GH_String(feat.GetFieldAsString(iField)), new GH_Path(i, iLayer, m)); } else { fset.Append(new GH_String("null"), new GH_Path(i, iLayer, m)); } } ///End get Feature Values ///Start get points if open polylines and points for (int gpc = 0; gpc < geom.GetPointCount(); gpc++) { ///Loop through geometry points for Rhino SRS double[] ogrPt = new double[3]; geom.GetPoint(gpc, ogrPt); Point3d pt3D = new Point3d(ogrPt[0], ogrPt[1], ogrPt[2]); pt3D.Transform(Heron.Convert.WGSToXYZTransform()); gset.Append(new GH_Point(pt3D), new GH_Path(i, iLayer, m)); ///Loop through geometry points for User SRS double[] ogrPtUser = new double[3]; geomUser.GetPoint(gpc, ogrPtUser); Point3d pt3DUser = new Point3d(ogrPtUser[0], ogrPtUser[1], ogrPtUser[2]); pt3DUser.Transform(userSRSToModelTransform); gsetUser.Append(new GH_Point(pt3DUser), new GH_Path(i, iLayer, m)); ///End loop through geometry points /// Get Feature Values if (fset.PathExists(new GH_Path(i, iLayer, m))) { fset.get_Branch(new GH_Path(i, iLayer, m)).Clear(); } for (int iField = 0; iField < feat.GetFieldCount(); iField++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iField); if (feat.IsFieldSet(iField)) { fset.Append(new GH_String(feat.GetFieldAsString(iField)), new GH_Path(i, iLayer, m)); } else { fset.Append(new GH_String("null"), new GH_Path(i, iLayer, m)); } } ///End Get Feature Values } ///End getting points if open polylines or points ///Start getting points if closed polylines and multipolygons for (int gi = 0; gi < geom.GetGeometryCount(); gi++) { sub_geom = geom.GetGeometryRef(gi); OSGeo.OGR.Geometry subsub_geom; sub_geomUser = geomUser.GetGeometryRef(gi); OSGeo.OGR.Geometry subsub_geomUser; if (sub_geom.GetGeometryCount() > 0) { for (int n = 0; n < sub_geom.GetGeometryCount(); n++) { subsub_geom = sub_geom.GetGeometryRef(n); subsub_geomUser = sub_geomUser.GetGeometryRef(n); for (int ptnum = 0; ptnum < subsub_geom.GetPointCount(); ptnum++) { ///Loop through geometry points double[] ogrPt = new double[3]; subsub_geom.GetPoint(ptnum, ogrPt); Point3d pt3D = new Point3d(ogrPt[0], ogrPt[1], ogrPt[2]); pt3D.Transform(Heron.Convert.WGSToXYZTransform()); gset.Append(new GH_Point(pt3D), new GH_Path(i, iLayer, m, gi, n)); ///Loop through geometry points for User SRS double[] ogrPtUser = new double[3]; subsub_geomUser.GetPoint(ptnum, ogrPtUser); Point3d pt3DUser = new Point3d(ogrPtUser[0], ogrPtUser[1], ogrPtUser[2]); pt3DUser.Transform(userSRSToModelTransform); gsetUser.Append(new GH_Point(pt3DUser), new GH_Path(i, iLayer, m, gi, n)); ///End loop through geometry points } subsub_geom.Dispose(); subsub_geomUser.Dispose(); } } else { for (int ptnum = 0; ptnum < sub_geom.GetPointCount(); ptnum++) { ///Loop through geometry points double[] ogrPt = new double[3]; sub_geom.GetPoint(ptnum, ogrPt); Point3d pt3D = new Point3d(ogrPt[0], ogrPt[1], ogrPt[2]); pt3D.Transform(Heron.Convert.WGSToXYZTransform()); gset.Append(new GH_Point(pt3D), new GH_Path(i, iLayer, m, gi)); ///Loop through geometry points for User SRS double[] ogrPtUser = new double[3]; sub_geomUser.GetPoint(ptnum, ogrPtUser); Point3d pt3DUser = new Point3d(ogrPtUser[0], ogrPtUser[1], ogrPtUser[2]); pt3DUser.Transform(userSRSToModelTransform); gsetUser.Append(new GH_Point(pt3DUser), new GH_Path(i, iLayer, m, gi)); ///End loop through geometry points } } sub_geom.Dispose(); sub_geomUser.Dispose(); /// Get Feature Values if (fset.PathExists(new GH_Path(i, iLayer, m))) { fset.get_Branch(new GH_Path(i, iLayer, m)).Clear(); } for (int iField = 0; iField < feat.GetFieldCount(); iField++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iField); if (feat.IsFieldSet(iField)) { fset.Append(new GH_String(feat.GetFieldAsString(iField)), new GH_Path(i, iLayer, m)); } else { fset.Append(new GH_String("null"), new GH_Path(i, iLayer, m)); } } ///End Get Feature Values } //m++; } m++; geom.Dispose(); geomUser.Dispose(); feat.Dispose(); } ///end while loop through features } ///end clipped layer else statement } ///end loop through boundaries layer.Dispose(); }///end loop through layers dataSource.Dispose(); DA.SetDataTree(0, recs); DA.SetDataTree(1, spatialReferences); DA.SetDataTree(2, fnames); DA.SetDataTree(3, fset); DA.SetDataTree(4, gset); DA.SetDataTree(5, gsetUser); DA.SetDataTree(6, recsUser); DA.SetDataTree(7, gGoo); DA.SetDataTree(8, gtype); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { Curve boundary = null; DA.GetData <Curve>(0, ref boundary); int zoom = -1; DA.GetData <int>(1, ref zoom); string filePath = string.Empty; DA.GetData <string>(2, ref filePath); if (!filePath.EndsWith(@"\")) { filePath = filePath + @"\"; } string prefix = string.Empty; DA.GetData <string>(3, ref prefix); if (prefix == "") { prefix = mbSource; } string URL = mbURL; string mbToken = string.Empty; DA.GetData <string>(4, ref mbToken); if (mbToken == "") { string hmbToken = System.Environment.GetEnvironmentVariable("HERONMAPBOXTOKEN"); if (hmbToken != null) { mbToken = hmbToken; AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Using Mapbox token stored in Environment Variable HERONMAPBOXTOKEN."); } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No Mapbox token is specified. Please get a valid token from mapbox.com"); return; } } bool run = false; DA.GetData <bool>("Run", ref run); ///GDAL setup RESTful.GdalConfiguration.ConfigureOgr(); OSGeo.OGR.Ogr.RegisterAll(); RESTful.GdalConfiguration.ConfigureGdal(); GH_Curve imgFrame; GH_String tCount; GH_Structure <GH_String> fnames = new GH_Structure <GH_String>(); GH_Structure <GH_String> fvalues = new GH_Structure <GH_String>(); GH_Structure <IGH_GeometricGoo> gGoo = new GH_Structure <IGH_GeometricGoo>(); GH_Structure <GH_String> gtype = new GH_Structure <GH_String>(); GH_Structure <IGH_GeometricGoo> gGooBuildings = new GH_Structure <IGH_GeometricGoo>(); int tileTotalCount = 0; int tileDownloadedCount = 0; ///Get image frame for given boundary if (!boundary.GetBoundingBox(true).IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Boundary is not valid."); return; } BoundingBox boundaryBox = boundary.GetBoundingBox(true); //create cache folder for vector tiles string cacheLoc = filePath + @"HeronCache\"; List <string> cachefilePaths = new List <string>(); if (!Directory.Exists(cacheLoc)) { Directory.CreateDirectory(cacheLoc); } //tile bounding box array List <Point3d> boxPtList = new List <Point3d>(); //get the tile coordinates for all tiles within boundary var ranges = Convert.GetTileRange(boundaryBox, zoom); var x_range = ranges.XRange; var y_range = ranges.YRange; if (x_range.Length > 100 || y_range.Length > 100) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "This tile range is too big (more than 100 tiles in the x or y direction). Check your units."); return; } ///cycle through tiles to get bounding box List <Polyline> tileExtents = new List <Polyline>(); List <double> tileHeight = new List <double>(); List <double> tileWidth = new List <double>(); for (int y = (int)y_range.Min; y <= y_range.Max; y++) { for (int x = (int)x_range.Min; x <= x_range.Max; x++) { //add bounding box of tile to list for translation Polyline tileExtent = Heron.Convert.GetTileAsPolygon(zoom, y, x); tileExtents.Add(tileExtent); tileWidth.Add(tileExtent[0].DistanceTo(tileExtent[1])); tileHeight.Add(tileExtent[1].DistanceTo(tileExtent[2])); boxPtList.AddRange(tileExtent.ToList()); cachefilePaths.Add(cacheLoc + mbSource.Replace(" ", "") + zoom + "-" + x + "-" + y + ".mvt"); tileTotalCount = tileTotalCount + 1; } } tCount = new GH_String(tileTotalCount + " tiles (" + tileDownloadedCount + " downloaded / " + (tileTotalCount - tileDownloadedCount) + " cached)"); ///bounding box of tile boundaries BoundingBox bboxPts = new BoundingBox(boxPtList); ///convert bounding box to polyline List <Point3d> imageCorners = bboxPts.GetCorners().ToList(); imageCorners.Add(imageCorners[0]); imgFrame = new GH_Curve(new Rhino.Geometry.Polyline(imageCorners).ToNurbsCurve()); ///tile range as string for (de)serialization of TileCacheMeta string tileRangeString = "Tile range for zoom " + zoom.ToString() + ": " + x_range[0].ToString() + "-" + y_range[0].ToString() + " to " + x_range[1].ToString() + "-" + y_range[1].ToString(); AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, tileRangeString); ///Query Mapbox URL ///download all tiles within boundary ///API to query string mbURLauth = mbURL + mbToken; if (run == true) { for (int y = (int)y_range.Min; y <= (int)y_range.Max; y++) { for (int x = (int)x_range.Min; x <= (int)x_range.Max; x++) { //create tileCache name string tileCache = mbSource.Replace(" ", "") + zoom + "-" + x + "-" + y + ".mvt"; string tileCacheLoc = cacheLoc + tileCache; //check cache folder to see if tile image exists locally if (File.Exists(tileCacheLoc)) { } else { string urlAuth = Heron.Convert.GetZoomURL(x, y, zoom, mbURLauth); System.Net.WebClient client = new System.Net.WebClient(); client.DownloadFile(urlAuth, tileCacheLoc); client.Dispose(); ///https://gdal.org/development/rfc/rfc59.1_utilities_as_a_library.html ///http://osgeo-org.1560.x6.nabble.com/gdal-dev-How-to-convert-shapefile-to-geojson-using-c-bindings-td5390953.html#a5391028 ///ogr2ogr is slow //OSGeo.GDAL.Dataset httpDS = OSGeo.GDAL.Gdal.OpenEx("MVT:"+urlAuth,4,null,null,null); //var transOptions = new OSGeo.GDAL.GDALVectorTranslateOptions(new[] { "-s_srs","EPSG:3857", "-t_srs", "EPSG:4326","-skipfailures" }); //var transDS = OSGeo.GDAL.Gdal.wrapper_GDALVectorTranslateDestName(mvtLoc + zoom + "-" + x + "-" + y , httpDS, transOptions, null, null); //httpDS.Dispose(); //transDS.Dispose(); tileDownloadedCount = tileDownloadedCount + 1; } } } } //add to tile count total tCount = new GH_String(tileTotalCount + " tiles (" + tileDownloadedCount + " downloaded / " + (tileTotalCount - tileDownloadedCount) + " cached)"); AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, tCount.ToString()); ///Build a VRT file ///https://stackoverflow.com/questions/55386597/gdal-c-sharp-wrapper-for-vrt-doesnt-write-a-vrt-file //string vrtFile = cacheLoc + "mapboxvector.vrt"; //AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, vrtFile); //var vrtOptions = new OSGeo.GDAL.GDALBuildVRTOptions(new[] { "-overwrite" }); //var vrtDataset = OSGeo.GDAL.Gdal.wrapper_GDALBuildVRT_names(vrtFile, cachefilePaths.ToArray(), vrtOptions, null, null); //vrtDataset.Dispose(); ///Set transform from input spatial reference to Rhino spatial reference ///TODO: look into adding a step for transforming to CRS set in SetCRS OSGeo.OSR.SpatialReference rhinoSRS = new OSGeo.OSR.SpatialReference(""); rhinoSRS.SetWellKnownGeogCS("WGS84"); ///TODO: verify the userSRS is valid ///TODO: use this as override of global SetSRS OSGeo.OSR.SpatialReference userSRS = new OSGeo.OSR.SpatialReference(""); //userSRS.SetFromUserInput(userSRStext); userSRS.SetFromUserInput("WGS84"); OSGeo.OSR.SpatialReference sourceSRS = new SpatialReference(""); sourceSRS.SetFromUserInput("EPSG:3857"); ///These transforms move and scale in order to go from userSRS to XYZ and vice versa Transform userSRSToModelTransform = Heron.Convert.GetUserSRSToModelTransform(userSRS); Transform modelToUserSRSTransform = Heron.Convert.GetModelToUserSRSTransform(userSRS); Transform sourceToModelSRSTransform = Heron.Convert.GetUserSRSToModelTransform(sourceSRS); Transform modelToSourceSRSTransform = Heron.Convert.GetModelToUserSRSTransform(sourceSRS); //OSGeo.GDAL.Driver gdalOGR = OSGeo.GDAL.Gdal.GetDriverByName("VRT"); //var ds = OSGeo.GDAL.Gdal.OpenEx(vrtFile, 4, ["VRT","MVT"], null, null); int t = 0; foreach (string mvtTile in cachefilePaths)// cachefilePaths) { OSGeo.OGR.Driver drv = OSGeo.OGR.Ogr.GetDriverByName("MVT"); OSGeo.OGR.DataSource ds = OSGeo.OGR.Ogr.Open("MVT:" + mvtTile, 0); string[] mvtOptions = new[] { "CLIP", "NO" }; //OSGeo.OGR.DataSource ds = drv.Open(mvtTile, 0); if (ds == null) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The vector datasource was unreadable by this component. It may not a valid file type for this component or otherwise null/empty."); return; } ///Morph raw mapbox tile points to geolocated tile Vector3d moveDir = tileExtents[t].ElementAt(0) - new Point3d(0, 0, 0); Transform move = Transform.Translation(moveDir); Transform scale = Transform.Scale(Plane.WorldXY, tileWidth[t] / 4096, tileHeight[t] / 4096, 1); Transform scaleMove = Transform.Multiply(move, scale); for (int iLayer = 0; iLayer < ds.GetLayerCount(); iLayer++) { OSGeo.OGR.Layer layer = ds.GetLayerByIndex(iLayer); long count = layer.GetFeatureCount(1); int featureCount = System.Convert.ToInt32(count); AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Layer #" + iLayer + " " + layer.GetName() + " has " + featureCount + " features"); //if (layer.GetName() == "admin" || layer.GetName() == "building") //{ OSGeo.OGR.FeatureDefn def = layer.GetLayerDefn(); ///Get the field names List <string> fieldnames = new List <string>(); for (int iAttr = 0; iAttr < def.GetFieldCount(); iAttr++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iAttr); fnames.Append(new GH_String(fdef.GetNameRef()), new GH_Path(iLayer, t)); } ///Loop through geometry OSGeo.OGR.Feature feat; int m = 0; ///error "Self-intersection at or near point..." when zoom gets below 12 for water ///this is an issue with the way mvt simplifies geometries at lower zoom levels and is a known problem ///TODO: look into how to fix invalid geom and return to the typical while loop iterating method //while ((feat = layer.GetNextFeature()) != null) while (true) { try { feat = layer.GetNextFeature(); } catch { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Some features had invalid geometry and were skipped."); continue; } if (feat == null) { break; } OSGeo.OGR.Geometry geom = feat.GetGeometryRef(); ///reproject geometry to WGS84 and userSRS ///TODO: look into using the SetCRS global variable here gtype.Append(new GH_String(geom.GetGeometryName()), new GH_Path(iLayer, t, m)); Transform tr = scaleMove; // new Transform(1); if (feat.GetGeometryRef() != null) { ///Convert GDAL geometries to IGH_GeometricGoo foreach (IGH_GeometricGoo gMorphed in Heron.Convert.OgrGeomToGHGoo(geom, tr)) { //gMorphed.Morph(morph); gGoo.Append(gMorphed, new GH_Path(iLayer, t, m)); } if (layer.GetName() == "building") { if (feat.GetFieldAsString(def.GetFieldIndex("extrude")) == "true") { double unitsConversion = Rhino.RhinoMath.UnitScale(Rhino.RhinoDoc.ActiveDoc.ModelUnitSystem, Rhino.UnitSystem.Meters); double height = System.Convert.ToDouble(feat.GetFieldAsString(def.GetFieldIndex("height"))) / unitsConversion; double min_height = System.Convert.ToDouble(feat.GetFieldAsString(def.GetFieldIndex("min_height"))) / unitsConversion; bool underground = System.Convert.ToBoolean(feat.GetFieldAsString(def.GetFieldIndex("underground"))); if (geom.GetGeometryType() == wkbGeometryType.wkbPolygon) { Extrusion bldg = Heron.Convert.OgrPolygonToExtrusion(geom, tr, height, min_height, underground); IGH_GeometricGoo bldgGoo = GH_Convert.ToGeometricGoo(bldg); gGooBuildings.Append(bldgGoo, new GH_Path(iLayer, t, m)); } if (geom.GetGeometryType() == wkbGeometryType.wkbMultiPolygon) { List <Extrusion> bldgs = Heron.Convert.OgrMultiPolyToExtrusions(geom, tr, height, min_height, underground); foreach (Extrusion bldg in bldgs) { IGH_GeometricGoo bldgGoo = GH_Convert.ToGeometricGoo(bldg); gGooBuildings.Append(bldgGoo, new GH_Path(iLayer, t, m)); } } } } /// Get Feature Values if (fvalues.PathExists(new GH_Path(iLayer, t, m))) { //fvalues.get_Branch(new GH_Path(iLayer, t, m)).Clear(); } for (int iField = 0; iField < feat.GetFieldCount(); iField++) { OSGeo.OGR.FieldDefn fdef = def.GetFieldDefn(iField); if (feat.IsFieldSet(iField)) { fvalues.Append(new GH_String(feat.GetFieldAsString(iField)), new GH_Path(iLayer, t, m)); } else { fvalues.Append(new GH_String("null"), new GH_Path(iLayer, t, m)); } } } m++; geom.Dispose(); feat.Dispose(); }///end while loop through features //}///end layer by name layer.Dispose(); }///end loop through layers ds.Dispose(); t++; }///end loop through mvt tiles //write out new tile range metadata for serialization TileCacheMeta = tileRangeString; DA.SetData(0, imgFrame); DA.SetDataTree(1, fnames); DA.SetDataTree(2, fvalues); DA.SetDataTree(3, gGoo); DA.SetDataList(4, "copyright Mapbox"); DA.SetDataTree(5, gtype); DA.SetDataTree(6, gGooBuildings); DA.SetDataList(7, tileExtents); }