| | 1 | | using System; |
| | 2 | | using System.Linq; |
| | 3 | | using System.Xml; |
| | 4 | | using System.Text; |
| | 5 | | using Morpho25.Utility; |
| | 6 | | using Morpho25.Settings; |
| | 7 | | using Morpho25.Geometry; |
| | 8 | | using System.Collections.Generic; |
| | 9 | | using System.Data; |
| | 10 | |
|
| | 11 | | namespace Morpho25.IO |
| | 12 | | { |
| | 13 | | /// <summary> |
| | 14 | | /// INX File class. |
| | 15 | | /// </summary> |
| | 16 | | public class Inx |
| | 17 | | { |
| | 18 | | const char NEWLINE = '\n'; |
| | 19 | | const string VERSION = "404"; |
| | 20 | | const string CHECK_SUM = "6104088"; |
| | 21 | | /// <summary> |
| | 22 | | /// Model. |
| | 23 | | /// </summary> |
| 0 | 24 | | public Model Model { get; } |
| | 25 | | /// <summary> |
| | 26 | | /// Matrix 2D of the terrain. |
| | 27 | | /// </summary> |
| 0 | 28 | | public string TerrainMatrix { get; private set; } |
| | 29 | | /// <summary> |
| | 30 | | /// Matrix 2D of the soils. |
| | 31 | | /// </summary> |
| 0 | 32 | | public string SoilMatrix { get; set; } |
| | 33 | |
|
| | 34 | | /// <summary> |
| | 35 | | /// Create a new INX File. |
| | 36 | | /// </summary> |
| | 37 | | /// <param name="model">Model.</param> |
| 0 | 38 | | public Inx(Model model) |
| 0 | 39 | | { |
| 0 | 40 | | Model = model; |
| 0 | 41 | | TerrainMatrix = EnvimetUtility.GetASCIImatrix( |
| 0 | 42 | | Model.EnvimetMatrix["terrainMatrix"]); |
| 0 | 43 | | SoilMatrix = EnvimetUtility.GetASCIImatrix( |
| 0 | 44 | | Model.EnvimetMatrix["soilMatrix"]); |
| 0 | 45 | | } |
| | 46 | | /// <summary> |
| | 47 | | /// Create a new INX File. 2.5D only. |
| | 48 | | /// </summary> |
| | 49 | | /// <param name="model">Model.</param> |
| | 50 | | /// <param name="ASCIIterrain">ASCII matrix for DEM.</param> |
| 0 | 51 | | public Inx(Model model, string ASCIIterrain) |
| 0 | 52 | | { |
| 0 | 53 | | Model = model; |
| 0 | 54 | | if (IsASCIIcorrect(ASCIIterrain)) |
| 0 | 55 | | TerrainMatrix = ASCIIterrain; |
| 0 | 56 | | SoilMatrix = EnvimetUtility.GetASCIImatrix( |
| 0 | 57 | | Model.EnvimetMatrix["soilMatrix"]); |
| | 58 | | // 2D only |
| 0 | 59 | | Model.IsDetailed = false; |
| 0 | 60 | | } |
| | 61 | |
|
| | 62 | | private bool IsASCIIcorrect(string ASCIImatrix) |
| 0 | 63 | | { |
| | 64 | | // ASCII envimet matrix are made by integers. I |
| | 65 | | // do not add validation for now. |
| | 66 | |
|
| 0 | 67 | | string[] yDirection = ASCIImatrix.Split('\n'); |
| 0 | 68 | | if (yDirection.Length != Model.Grid.Size.NumY) |
| 0 | 69 | | throw new ArgumentOutOfRangeException( |
| 0 | 70 | | $"{nameof(ASCIImatrix)} must contain {Model.Grid.Size.NumY} elements in Y."); |
| 0 | 71 | | foreach (string y in yDirection) |
| 0 | 72 | | { |
| 0 | 73 | | string[] xDirection = y.Split(','); |
| 0 | 74 | | if (xDirection.Length != Model.Grid.Size.NumX) |
| 0 | 75 | | throw new ArgumentOutOfRangeException( |
| 0 | 76 | | $"{nameof(ASCIImatrix)} must contain {Model.Grid.Size.NumX} elements in X."); |
| 0 | 77 | | } |
| 0 | 78 | | return true; |
| 0 | 79 | | } |
| | 80 | |
|
| | 81 | | /// <summary> |
| | 82 | | /// Write INX file |
| | 83 | | /// </summary> |
| | 84 | | /// <param name="fullPath">Full path of INX file to save</param> |
| | 85 | | public void WriteInx(string fullPath = null) |
| 0 | 86 | | { |
| 0 | 87 | | var now = DateTime.Now; |
| 0 | 88 | | string revisionDate = now.ToString("yyyy-MM-dd HH:mm:ss"); |
| | 89 | |
|
| | 90 | | // get objects |
| 0 | 91 | | Grid grid = Model.Grid; |
| 0 | 92 | | Location location = Model.Location; |
| 0 | 93 | | List<Terrain> terrains = Model.TerrainObjects; |
| 0 | 94 | | List<Building> buildings = Model.BuildingObjects; |
| 0 | 95 | | List<Plant3d> plant3d = Model.Plant3dObjects; |
| 0 | 96 | | List<Receptor> receptors = Model.ReceptorObjects; |
| | 97 | |
|
| | 98 | | // Is 3D? |
| 0 | 99 | | var is3D = Model.IsDetailed; |
| 0 | 100 | | var shiftVoxels = Model.ShiftEachVoxel; |
| | 101 | |
|
| | 102 | | // get ascii matrix |
| 0 | 103 | | string topMatrix = EnvimetUtility.GetASCIImatrix(Model.EnvimetMatrix["topMatrix"]); |
| 0 | 104 | | string bottomMatrix = EnvimetUtility.GetASCIImatrix(Model.EnvimetMatrix["bottomMatrix"]); |
| 0 | 105 | | string IDmatrix = EnvimetUtility.GetASCIImatrix(Model.EnvimetMatrix["idMatrix"]); |
| 0 | 106 | | string zeroMatrix = EnvimetUtility.GetASCIImatrix(new Matrix2d(grid.Size.NumX, grid.Size.NumY, "0")); |
| | 107 | |
|
| 0 | 108 | | if (fullPath == null && Model.Workspace == null) |
| 0 | 109 | | { |
| 0 | 110 | | throw new Exception("Provide a full path of the INX file to save or add Workspace to Model."); |
| | 111 | | } |
| | 112 | |
|
| | 113 | | // start with xml |
| 0 | 114 | | XmlTextWriter xWriter = new XmlTextWriter(fullPath ?? Model.Workspace.ModelPath, Encoding.UTF8); |
| 0 | 115 | | xWriter.WriteStartElement("ENVI-MET_Datafile"); |
| 0 | 116 | | xWriter.WriteString(NEWLINE + " "); |
| | 117 | |
|
| 0 | 118 | | string[] empty = { }; |
| | 119 | |
|
| 0 | 120 | | string headerTitle = "Header"; |
| 0 | 121 | | string[] headerTag = new string[] { "filetype", "version", "revisiondate", "remark", "checksum", "encryption |
| 0 | 122 | | string[] headerValue = new string[] { "INPX ENVI-met Area Input File", VERSION, revisionDate, "Created with |
| 0 | 123 | | Util.CreateXmlSection(xWriter, headerTitle, headerTag, headerValue, 0, empty); |
| | 124 | |
|
| 0 | 125 | | string baseDataTitle = "baseData"; |
| 0 | 126 | | string[] baseDataTag = new string[] { "modelDescription", "modelAuthor", "modelcopyright" }; |
| 0 | 127 | | string[] baseDataValue = new string[] { " A brave new area ", " Grasshopper envimet ", "The creator or distr |
| 0 | 128 | | Util.CreateXmlSection(xWriter, baseDataTitle, baseDataTag, baseDataValue, 0, empty); |
| | 129 | |
|
| 0 | 130 | | string useSplitting = null; |
| 0 | 131 | | string verticalStretch = null; |
| 0 | 132 | | string startStretch = null; |
| 0 | 133 | | string gridsZ = (grid.IsSplitted) |
| 0 | 134 | | ? (grid.Size.NumZ - 4).ToString() |
| 0 | 135 | | : grid.Size.NumZ.ToString(); |
| 0 | 136 | | string useTelescoping = null; |
| 0 | 137 | | string gridsI = (grid.Size.NumX).ToString(); |
| 0 | 138 | | string gridsJ = (grid.Size.NumY).ToString(); |
| 0 | 139 | | string[] attribute2dElements = { "matrix-data", gridsI, gridsJ }; |
| 0 | 140 | | string dx = grid.Size.DimX.ToString("n5"); |
| 0 | 141 | | string dy = grid.Size.DimY.ToString("n5"); |
| 0 | 142 | | string dz = grid.Size.DimZ.ToString("n5"); |
| | 143 | |
|
| 0 | 144 | | if (grid.Telescope > 0) |
| 0 | 145 | | { |
| 0 | 146 | | useTelescoping = "1"; |
| 0 | 147 | | useSplitting = "0"; |
| 0 | 148 | | verticalStretch = grid.Telescope.ToString("n5"); |
| 0 | 149 | | startStretch = grid.StartTelescopeHeight.ToString("n5"); |
| 0 | 150 | | if (grid.CombineGridType) |
| 0 | 151 | | useSplitting = "1"; |
| 0 | 152 | | } |
| | 153 | | else |
| 0 | 154 | | { |
| 0 | 155 | | useTelescoping = "0"; |
| 0 | 156 | | useSplitting = "1"; |
| 0 | 157 | | verticalStretch = "0"; |
| 0 | 158 | | startStretch = "0"; |
| 0 | 159 | | } |
| | 160 | |
|
| 0 | 161 | | string modelGeometryTitle = "modelGeometry"; |
| 0 | 162 | | string[] modelGeometryTag = new string[] { "grids-I", "grids-J", "grids-Z", "dx", "dy", "dz-base", "useTeles |
| 0 | 163 | | string[] modelGeometryValue = new string[] { gridsI, gridsJ, gridsZ, dx, dy, dz, useTelescoping, useSplittin |
| 0 | 164 | | Util.CreateXmlSection(xWriter, modelGeometryTitle, modelGeometryTag, modelGeometryValue, 0, empty); |
| | 165 | |
|
| 0 | 166 | | string nestingAreaTitle = "nestingArea"; |
| 0 | 167 | | string[] nestingAreaTag = new string[] { "numberNestinggrids", "soilProfileA", "soilProfileB" }; |
| 0 | 168 | | string[] nestingAreaValue = new string[] { grid.NestingGrids.NumberOfCells.ToString(), grid.NestingGrids.Fir |
| 0 | 169 | | Util.CreateXmlSection(xWriter, nestingAreaTitle, nestingAreaTag, nestingAreaValue, 0, empty); |
| | 170 | |
|
| 0 | 171 | | string locationDataTitle = "locationData"; |
| | 172 | |
|
| 0 | 173 | | string utmZone = " ", realworldLowerLeftX = Location.REALWORLD_POINT, realworldLowerLeftY = Location.REALWOR |
| | 174 | |
|
| 0 | 175 | | if (location.UTM != null) |
| 0 | 176 | | { |
| 0 | 177 | | utmZone = location.UTM.UTMzone; |
| 0 | 178 | | realworldLowerLeftX = location.UTM.UTMesting.ToString(); |
| 0 | 179 | | realworldLowerLeftY = location.UTM.UTMnorthing.ToString(); |
| 0 | 180 | | } |
| | 181 | |
|
| 0 | 182 | | string[] locationDataTag = new string[] { "modelRotation", "projectionSystem", "UTMZone", "realworldLowerLef |
| 0 | 183 | | string[] locationDataValue = new string[] { location.ModelRotation.ToString("n5"), Location.PROJECTION_SYSTE |
| 0 | 184 | | Util.CreateXmlSection(xWriter, locationDataTitle, locationDataTag, locationDataValue, 0, empty); |
| | 185 | |
|
| 0 | 186 | | string defaultSettingsTitle = "defaultSettings"; |
| 0 | 187 | | string[] defaultSettingsTag = new string[] { "commonWallMaterial", "commonRoofMaterial" }; |
| 0 | 188 | | string[] defaultSettingsValue = new string[] { Material.DEFAULT_WALL, Material.DEFAULT_ROOF }; |
| 0 | 189 | | Util.CreateXmlSection(xWriter, defaultSettingsTitle, defaultSettingsTag, defaultSettingsValue, 0, empty); |
| | 190 | |
|
| 0 | 191 | | string buildings2DTitle = "buildings2D"; |
| 0 | 192 | | string[] buildings2DTag = new string[] { "zTop", "zBottom", "buildingNr", "fixedheight" }; |
| 0 | 193 | | string[] buildings2DValue = new string[] { NEWLINE + topMatrix, NEWLINE + bottomMatrix, NEWLINE + IDmatrix, |
| 0 | 194 | | Util.CreateXmlSection(xWriter, buildings2DTitle, buildings2DTag, buildings2DValue, 1, attribute2dElements); |
| | 195 | |
|
| 0 | 196 | | if (Model.Plant2dObjects.Count > 0) |
| 0 | 197 | | { |
| 0 | 198 | | string plantMatrix = EnvimetUtility.GetASCIImatrix(Model.EnvimetMatrix["plantMatrix"]); |
| 0 | 199 | | string simpleplants2DTitle = "simpleplants2D"; |
| 0 | 200 | | string[] simpleplants2DTag = new string[] { "ID_plants1D" }; |
| 0 | 201 | | string[] simpleplants2DValue = new string[] { NEWLINE + plantMatrix }; |
| 0 | 202 | | Util.CreateXmlSection(xWriter, simpleplants2DTitle, simpleplants2DTag, simpleplants2DValue, 1, attribute |
| 0 | 203 | | } |
| | 204 | |
|
| 0 | 205 | | if (plant3d.Count > 0) |
| 0 | 206 | | { |
| 0 | 207 | | foreach (Plant3d plant in plant3d) |
| 0 | 208 | | { |
| 0 | 209 | | string plants3DTitle = "3Dplants"; |
| 0 | 210 | | string[] plants3DTag = new string[] { "rootcell_i", "rootcell_j", "rootcell_k", "plantID", "name", " |
| 0 | 211 | | string[] plants3DValue = new string[] { plant.Pixel.I.ToString(), plant.Pixel.J.ToString(), plant.Pi |
| 0 | 212 | | Util.CreateXmlSection(xWriter, plants3DTitle, plants3DTag, plants3DValue, 0, empty); |
| 0 | 213 | | } |
| 0 | 214 | | } |
| | 215 | |
|
| 0 | 216 | | if (receptors.Count > 0) |
| 0 | 217 | | { |
| 0 | 218 | | foreach (Receptor receptor in receptors) |
| 0 | 219 | | { |
| 0 | 220 | | string receptorsTitle = "Receptors"; |
| 0 | 221 | | string[] receptorsTag = new string[] { "cell_i", "cell_j", "name" }; |
| 0 | 222 | | string[] receptorsValue = new string[] { receptor.Pixel.I.ToString(), receptor.Pixel.J.ToString(), r |
| 0 | 223 | | Util.CreateXmlSection(xWriter, receptorsTitle, receptorsTag, receptorsValue, 0, empty); |
| 0 | 224 | | } |
| 0 | 225 | | } |
| | 226 | |
|
| 0 | 227 | | string soils2DTitle = "soils2D"; |
| 0 | 228 | | string[] soils2DTag = new string[] { "ID_soilprofile" }; |
| 0 | 229 | | string[] soils2DValue = new string[] { NEWLINE + SoilMatrix }; |
| 0 | 230 | | Util.CreateXmlSection(xWriter, soils2DTitle, soils2DTag, soils2DValue, 1, attribute2dElements); |
| | 231 | |
|
| 0 | 232 | | string demTitle = "dem"; |
| 0 | 233 | | string[] demDTag = new string[] { "terrainheight" }; |
| 0 | 234 | | string[] demValue = new string[] { NEWLINE + TerrainMatrix }; |
| 0 | 235 | | Util.CreateXmlSection(xWriter, demTitle, demDTag, demValue, 1, attribute2dElements); |
| | 236 | |
|
| 0 | 237 | | if (Model.SourceObjects.Count > 0) |
| 0 | 238 | | { |
| 0 | 239 | | string sourceMatrix = EnvimetUtility.GetASCIImatrix(Model.EnvimetMatrix["sourceMatrix"]); |
| 0 | 240 | | string sources2DTitle = "sources2D"; |
| 0 | 241 | | string[] sources2DTag = new string[] { "ID_sources" }; |
| 0 | 242 | | string[] sources2DValue = new string[] { NEWLINE + sourceMatrix }; |
| 0 | 243 | | Util.CreateXmlSection(xWriter, sources2DTitle, sources2DTag, sources2DValue, 1, attribute2dElements); |
| 0 | 244 | | } |
| | 245 | |
|
| 0 | 246 | | if (buildings.Count > 0) |
| 0 | 247 | | { |
| 0 | 248 | | foreach (Building building in buildings) |
| 0 | 249 | | { |
| 0 | 250 | | string bps = building.ObserveBPS ? "1" : "0"; |
| | 251 | |
|
| 0 | 252 | | string buildinginfoTitle = "Buildinginfo"; |
| 0 | 253 | | string[] buildinginfoTag = new string[] { "BuildingInternalNr", "BuildingName", "BuildingWallMateria |
| 0 | 254 | | string[] buildinginfoValue = new string[] { building.ID.ToString(), building.Name, building.Material |
| 0 | 255 | | Util.CreateXmlSection(xWriter, buildinginfoTitle, buildinginfoTag, buildinginfoValue, 0, empty); |
| 0 | 256 | | } |
| 0 | 257 | | } |
| | 258 | |
|
| 0 | 259 | | if (!is3D) |
| 0 | 260 | | { |
| 0 | 261 | | xWriter.WriteEndElement(); |
| 0 | 262 | | xWriter.Close(); |
| 0 | 263 | | return; |
| | 264 | | } |
| | 265 | |
|
| | 266 | | // 3D part |
| 0 | 267 | | var gridsK = grid.Size.NumZ.ToString(); |
| 0 | 268 | | string[] attribute3dElements = { "sparematrix-3D", gridsI, gridsJ, gridsK, "" }; |
| 0 | 269 | | string[] attribute3dBuildings3D = { "sparematrix-3D", gridsI, gridsJ, gridsK, "0" }; |
| 0 | 270 | | string[] attribute3dDem3D = { "sparematrix-3D", gridsI, gridsJ, gridsK, "0.00000" }; |
| | 271 | |
|
| 0 | 272 | | string modelGeometry3DTitle = "modelGeometry3D"; |
| 0 | 273 | | string[] modelGeometry3DTag = new string[] { "grids3D-I", "grids3D-J", "grids3D-K" }; |
| 0 | 274 | | string[] modelGeometry3DValue = new string[] { gridsI, gridsJ, gridsK }; |
| 0 | 275 | | Util.CreateXmlSection(xWriter, modelGeometry3DTitle, modelGeometry3DTag, modelGeometry3DValue, 0, empty); |
| | 276 | |
|
| | 277 | | // 3D ID |
| 0 | 278 | | var terrainPixels = terrains.SelectMany(_ => _.Pixels) |
| 0 | 279 | | .ToList(); |
| 0 | 280 | | if (!terrainPixels.Any()) terrainPixels = null; |
| | 281 | |
|
| 0 | 282 | | var idMatrix = new List<string>() { string.Empty }; |
| 0 | 283 | | var wallMatrix = new List<string>() { string.Empty }; |
| 0 | 284 | | var greenMatrix = new List<string>() { string.Empty }; |
| | 285 | |
|
| 0 | 286 | | foreach (var building in buildings) |
| 0 | 287 | | { |
| 0 | 288 | | building.SetMatrix3d(grid, terrainPixels, shiftVoxels); |
| 0 | 289 | | idMatrix.AddRange(building.BuildingIDrows); |
| 0 | 290 | | wallMatrix.AddRange(building.BuildingWallRows); |
| 0 | 291 | | greenMatrix.AddRange(building.BuildingGreenWallRows); |
| 0 | 292 | | } |
| 0 | 293 | | idMatrix.Add(string.Empty); |
| 0 | 294 | | wallMatrix.Add(string.Empty); |
| 0 | 295 | | greenMatrix.Add(string.Empty); |
| | 296 | |
|
| 0 | 297 | | string buildings3DTitle = "buildings3D"; |
| 0 | 298 | | string[] buildings3DTag = new string[] { "buildingFlagAndNr" }; |
| 0 | 299 | | string[] buildings3DValue = new string[] { String.Join("\n", idMatrix) }; |
| 0 | 300 | | Util.CreateXmlSection(xWriter, buildings3DTitle, buildings3DTag, buildings3DValue, 2, attribute3dBuildings3D |
| | 301 | |
|
| 0 | 302 | | var demMatrix = new List<string>() { string.Empty }; |
| 0 | 303 | | foreach (var terrain in terrains) |
| 0 | 304 | | { |
| 0 | 305 | | demMatrix.AddRange(terrain.TerrainIDrows); |
| 0 | 306 | | } |
| 0 | 307 | | demMatrix.Add(string.Empty); |
| | 308 | |
|
| 0 | 309 | | string dem3DTitle = "dem3D"; |
| 0 | 310 | | string[] dem3DTag = new string[] { "terrainflag" }; |
| 0 | 311 | | string[] dem3DValue = new string[] { String.Join("\n", demMatrix) }; |
| 0 | 312 | | Util.CreateXmlSection(xWriter, dem3DTitle, dem3DTag, dem3DValue, 2, attribute3dDem3D); |
| | 313 | |
|
| 0 | 314 | | string wallDBTitle = "WallDB"; |
| 0 | 315 | | string[] wallDBTag = new string[] { "ID_wallDB" }; |
| 0 | 316 | | string[] wallDBValue = new string[] { String.Join("\n", wallMatrix) }; |
| 0 | 317 | | Util.CreateXmlSection(xWriter, wallDBTitle, wallDBTag, wallDBValue, 2, attribute3dElements); |
| | 318 | |
|
| 0 | 319 | | string singleWallDBTitle = "SingleWallDB"; |
| 0 | 320 | | string[] singleWallDBTag = new string[] { "ID_singlewallDB" }; |
| 0 | 321 | | string[] singleWallDBValue = new string[] { "\n" }; |
| 0 | 322 | | Util.CreateXmlSection(xWriter, singleWallDBTitle, singleWallDBTag, singleWallDBValue, 2, attribute3dElements |
| | 323 | |
|
| 0 | 324 | | string greeningDBTitle = "GreeningDB"; |
| 0 | 325 | | string[] greeningDBTag = new string[] { "ID_GreeningDB" }; |
| 0 | 326 | | string[] greeningDBValue = new string[] { String.Join("\n", greenMatrix) }; |
| 0 | 327 | | Util.CreateXmlSection(xWriter, greeningDBTitle, greeningDBTag, greeningDBValue, 2, attribute3dElements); |
| | 328 | |
|
| 0 | 329 | | xWriter.WriteEndElement(); |
| 0 | 330 | | xWriter.Close(); |
| 0 | 331 | | } |
| | 332 | | } |
| | 333 | | } |