/// <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) { List <Curve> boundary = new List <Curve>(); DA.GetDataList <Curve>(0, boundary); int zoom = -1; DA.GetData <int>(1, ref zoom); string fileloc = ""; DA.GetData <string>(2, ref fileloc); if (!fileloc.EndsWith(@"\")) { fileloc = fileloc + @"\"; } string prefix = ""; DA.GetData <string>(3, ref prefix); if (prefix == "") { prefix = mbSource; } string URL = mbURL; //DA.GetData<string>(4, ref URL); string mbToken = ""; 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); GH_Structure <GH_String> mapList = new GH_Structure <GH_String>(); GH_Structure <GH_Curve> imgFrame = new GH_Structure <GH_Curve>(); GH_Structure <GH_String> tCount = new GH_Structure <GH_String>(); GH_Structure <GH_Mesh> tMesh = new GH_Structure <GH_Mesh>(); for (int i = 0; i < boundary.Count; i++) { GH_Path path = new GH_Path(i); int tileTotalCount = 0; int tileDownloadedCount = 0; ///Get image frame for given boundary if (!boundary[i].GetBoundingBox(true).IsValid) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Boundary is not valid."); return; } BoundingBox boundaryBox = boundary[i].GetBoundingBox(true); ///TODO: look into scaling boundary to get buffer tiles ///file path for final image string imgPath = fileloc + prefix + "_" + i + ".png"; //location of final image file mapList.Append(new GH_String(imgPath), path); //create cache folder for images string cacheLoc = fileloc + @"HeronCache\"; List <string> cacheFileLocs = 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); List <List <int> > tileList = new List <List <int> >(); 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 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 boxPtList.AddRange(Convert.GetTileAsPolygon(zoom, y, x).ToList()); cacheFileLocs.Add(cacheLoc + mbSource.Replace(" ", "") + zoom + x + y + ".png"); tileTotalCount = tileTotalCount + 1; } } tCount.Insert(new GH_String(tileTotalCount + " tiles (" + tileDownloadedCount + " downloaded / " + (tileTotalCount - tileDownloadedCount) + " cached)"), path, 0); //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.Append(new GH_Curve(new Rhino.Geometry.Polyline(imageCorners).ToNurbsCurve()), path); //tile range as string for (de)serialization of TileCacheMeta string tileRangeString = zoom.ToString() + x_range[0].ToString() + y_range[0].ToString() + x_range[1].ToString() + y_range[1].ToString(); //check if the existing final image already covers the boundary. //if so, no need to download more or reassemble the cached tiles. ///temporarily disable until how to tag images with meta data is figured out /* * if (TileCacheMeta == tileRangeString && Convert.CheckCacheImagesExist(cacheFileLocs)) * { * if (File.Exists(imgPath)) * { * using (Bitmap imageT = new Bitmap(imgPath)) * { * //System.Drawing.Imaging.PropertyItem prop = imageT.GetPropertyItem(40092); * //string imgComment = Encoding.Unicode.GetString(prop.Value); * string imgComment = imageT.GetCommentsFromImage(); * //AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, imgComment); * imageT.Dispose(); * //check to see if tilerange in comments matches current tilerange * if (imgComment == (mbSource.Replace(" ", "") + tileRangeString)) * { * AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "Using existing topo image."); * * Mesh eMesh = TopoMeshFromImage(imgPath, boundaryBox, zoom); * tMesh.Append(new GH_Mesh(eMesh), path); * continue; * } * * } * * } * * } */ ///Query Mapbox URL ///download all tiles within boundary ///merge tiles into one bitmap ///API to query ///string mbURL = "https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}@2x.pngraw?access_token=" + mbToken; string mbURLauth = mbURL + mbToken; ///Do the work of assembling image ///setup final image container bitmap int fImageW = ((int)x_range.Length + 1) * 512; int fImageH = ((int)y_range.Length + 1) * 512; Bitmap finalImage = new Bitmap(fImageW, fImageH, System.Drawing.Imaging.PixelFormat.Format32bppArgb); int imgPosW = 0; int imgPosH = 0; if (run == true) { using (Graphics g = Graphics.FromImage(finalImage)) { g.Clear(Color.Black); 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 + ".png"; string tileCahceLoc = cacheLoc + tileCache; //check cache folder to see if tile image exists locally if (File.Exists(tileCahceLoc)) { Bitmap tmpImage = new Bitmap(Image.FromFile(tileCahceLoc)); //add tmp image to final g.DrawImage(tmpImage, imgPosW * 512, imgPosH * 512); tmpImage.Dispose(); } else { tileList.Add(new List <int> { zoom, y, x }); string urlAuth = Convert.GetZoomURL(x, y, zoom, mbURLauth); System.Net.WebClient client = new System.Net.WebClient(); client.DownloadFile(urlAuth, tileCahceLoc); Bitmap tmpImage = new Bitmap(Image.FromFile(tileCahceLoc)); client.Dispose(); //add tmp image to final g.DrawImage(tmpImage, imgPosW * 512, imgPosH * 512); tmpImage.Dispose(); tileDownloadedCount = tileDownloadedCount + 1; } //increment x insert position, goes left to right imgPosW++; } //increment y insert position, goes top to bottom imgPosH++; imgPosW = 0; } //garbage collection g.Dispose(); //add tile range meta data to image comments finalImage.AddCommentsToPNG(mbSource.Replace(" ", "") + tileRangeString); //save out assembled image finalImage.Save(imgPath, System.Drawing.Imaging.ImageFormat.Png); } } //garbage collection finalImage.Dispose(); //add to tile count total tCount.Insert(new GH_String(tileTotalCount + " tiles (" + tileDownloadedCount + " downloaded / " + (tileTotalCount - tileDownloadedCount) + " cached)"), path, 0); Mesh nMesh = TopoMeshFromImage(imgPath, boundaryBox, zoom); //mesh.Flip(true, true, true); tMesh.Append(new GH_Mesh(nMesh), path); //write out new tile range metadata for serialization TileCacheMeta = tileRangeString; } DA.SetDataTree(0, mapList); DA.SetDataTree(1, imgFrame); DA.SetDataTree(2, tCount); DA.SetDataTree(3, tMesh); DA.SetDataList(4, "copyright Mapbox"); }