/// <summary> /// Gets the <see cref="BiomeType"/> associated with the given conditions. /// </summary> /// <param name="climateType">The climate type of the location.</param> /// <param name="humidityType">The humidity type of the location.</param> /// <param name="elevation">The elevation of the location above sea level.</param> /// <returns>The <see cref="BiomeType"/> associated with the given conditions.</returns> public static BiomeType GetBiomeType(ClimateType climateType, HumidityType humidityType, double elevation) { if (elevation <= 0) { return(BiomeType.Sea); } switch (climateType) { case ClimateType.Polar: return(elevation >= 0.15 ? BiomeType.Alpine : BiomeType.Polar); case ClimateType.Subpolar: return(elevation >= 0.15 ? BiomeType.Subalpine : BiomeType.Tundra); case ClimateType.Boreal: if (humidityType <= HumidityType.Arid) { return(BiomeType.LichenWoodland); } else { return(BiomeType.ConiferousForest); } case ClimateType.CoolTemperate: if (humidityType <= HumidityType.Perarid) { return(BiomeType.ColdDesert); } else if (humidityType == HumidityType.Arid) { return(BiomeType.Steppe); } else { return(BiomeType.MixedForest); } case ClimateType.WarmTemperate: if (humidityType <= HumidityType.Perarid) { return(BiomeType.HotDesert); } else if (humidityType <= HumidityType.Arid) { return(BiomeType.Shrubland); } else { return(BiomeType.DeciduousForest); } case ClimateType.Subtropical: if (humidityType <= HumidityType.Perarid) { return(BiomeType.HotDesert); } else if (humidityType == HumidityType.Arid) { return(BiomeType.Savanna); } else if (humidityType <= HumidityType.Subhumid) { return(BiomeType.MonsoonForest); } else { return(BiomeType.RainForest); } case ClimateType.Tropical: if (humidityType <= HumidityType.Perarid) { return(BiomeType.HotDesert); } else if (humidityType <= HumidityType.Semiarid) { return(BiomeType.Savanna); } else if (humidityType == HumidityType.Subhumid) { return(BiomeType.MonsoonForest); } else { return(BiomeType.RainForest); } default: return(BiomeType.HotDesert); } }
/// <summary> /// Initializes a new instance of <see cref="WeatherMaps"/>. /// </summary> /// <param name="planet">The planet being mapped.</param> /// <param name="elevationMap">An elevation map.</param> /// <param name="winterTemperatureMap">A winter temperature map.</param> /// <param name="summerTemperatureMap">A summer temperature map.</param> /// <param name="precipitationMap">A precipitation map.</param> /// <param name="resolution">The intended vertical resolution of the maps.</param> /// <param name="options"> /// The map projection options to use. All the map images must have been generated using /// these same options, or thew results will not be accurate. /// </param> public WeatherMaps( Planetoid planet, Image <L16> elevationMap, Image <L16> winterTemperatureMap, Image <L16> summerTemperatureMap, Image <L16> precipitationMap, int resolution, MapProjectionOptions?options = null) { var projection = options ?? MapProjectionOptions.Default; XLength = (int)Math.Floor(projection.AspectRatio * resolution); YLength = resolution; BiomeMap = new BiomeType[XLength][]; ClimateMap = new ClimateType[XLength][]; var humidityMap = new HumidityType[XLength][]; SeaIceRangeMap = new FloatRange[XLength][]; for (var x = 0; x < XLength; x++) { BiomeMap[x] = new BiomeType[YLength]; ClimateMap[x] = new ClimateType[YLength]; humidityMap[x] = new HumidityType[YLength]; SeaIceRangeMap[x] = new FloatRange[YLength]; } var scale = SurfaceMap.GetScale(resolution, projection.Range); var stretch = scale / projection.ScaleFactor; var elevationScale = SurfaceMap.GetScale(elevationMap.Height, projection.Range); var winterScale = winterTemperatureMap.Height == elevationMap.Height ? elevationScale : SurfaceMap.GetScale(winterTemperatureMap.Height, projection.Range); var summerScale = summerTemperatureMap.Height == elevationMap.Height ? elevationScale : SurfaceMap.GetScale(summerTemperatureMap.Height, projection.Range); var precipitationScale = precipitationMap.Height == elevationMap.Height ? elevationScale : SurfaceMap.GetScale(precipitationMap.Height, projection.Range); var totalElevation = 0.0; var minTemperature = 5000.0f; var maxTemperature = 0.0f; var totalTemperature = 0.0f; var totalPrecipiation = 0.0; var xToEX = new Dictionary <int, int>(); var xToWX = new Dictionary <int, int>(); var xToSX = new Dictionary <int, int>(); var xToPX = new Dictionary <int, int>(); for (var y = 0; y < YLength; y++) { var latitude = projection.EqualArea ? SurfaceMap.GetLatitudeOfCylindricalEqualAreaProjection(y, resolution, scale, projection) : SurfaceMap.GetLatitudeOfEquirectangularProjection(y, resolution, scale, projection); var elevationY = projection.EqualArea ? SurfaceMap.GetCylindricalEqualAreaYFromLatWithScale(latitude, elevationMap.Height, elevationScale, projection) : SurfaceMap.GetEquirectangularYFromLatWithScale(latitude, elevationMap.Height, elevationScale, projection); var elevationSpan = elevationMap.GetPixelRowSpan(elevationY); int winterY; if (winterTemperatureMap.Height == elevationMap.Height) { winterY = elevationY; } else if (projection.EqualArea) { winterY = SurfaceMap.GetCylindricalEqualAreaYFromLatWithScale(latitude, winterTemperatureMap.Height, winterScale, projection); } else { winterY = SurfaceMap.GetEquirectangularYFromLatWithScale(latitude, winterTemperatureMap.Height, winterScale, projection); } var winterSpan = winterTemperatureMap.GetPixelRowSpan(winterY); int summerY; if (summerTemperatureMap.Height == elevationMap.Height) { summerY = elevationY; } else if (projection.EqualArea) { summerY = SurfaceMap.GetCylindricalEqualAreaYFromLatWithScale(latitude, summerTemperatureMap.Height, summerScale, projection); } else { summerY = SurfaceMap.GetEquirectangularYFromLatWithScale(latitude, summerTemperatureMap.Height, summerScale, projection); } var summerSpan = summerTemperatureMap.GetPixelRowSpan(summerY); int precipitationY; if (precipitationMap.Height == elevationMap.Height) { precipitationY = elevationY; } else if (projection.EqualArea) { precipitationY = SurfaceMap.GetCylindricalEqualAreaYFromLatWithScale(latitude, precipitationMap.Height, precipitationScale, projection); } else { precipitationY = SurfaceMap.GetEquirectangularYFromLatWithScale(latitude, precipitationMap.Height, precipitationScale, projection); } var precipitationSpan = precipitationMap.GetPixelRowSpan(precipitationY); for (var x = 0; x < XLength; x++) { if (!xToEX.TryGetValue(x, out var elevationX)) { var longitude = projection.EqualArea ? SurfaceMap.GetLongitudeOfCylindricalEqualAreaProjection(x, XLength, scale, projection) : SurfaceMap.GetLongitudeOfEquirectangularProjection(x, XLength, stretch, projection); elevationX = projection.EqualArea ? SurfaceMap.GetCylindricalEqualAreaXFromLonWithScale(longitude, elevationMap.Width, elevationScale, projection) : SurfaceMap.GetEquirectangularXFromLonWithScale(longitude, elevationMap.Width, elevationScale, projection); int wX; if (winterTemperatureMap.Width == elevationMap.Width) { wX = elevationX; } else if (projection.EqualArea) { wX = SurfaceMap.GetCylindricalEqualAreaXFromLonWithScale(longitude, winterTemperatureMap.Width, winterScale, projection); } else { wX = SurfaceMap.GetEquirectangularXFromLonWithScale(longitude, winterTemperatureMap.Width, winterScale, projection); } int sX; if (summerTemperatureMap.Width == elevationMap.Width) { sX = elevationX; } else if (projection.EqualArea) { sX = SurfaceMap.GetCylindricalEqualAreaXFromLonWithScale(longitude, summerTemperatureMap.Width, summerScale, projection); } else { sX = SurfaceMap.GetEquirectangularXFromLonWithScale(longitude, summerTemperatureMap.Width, summerScale, projection); } int pX; if (precipitationMap.Width == elevationMap.Width) { pX = elevationX; } else if (projection.EqualArea) { pX = SurfaceMap.GetCylindricalEqualAreaXFromLonWithScale(longitude, precipitationMap.Width, precipitationScale, projection); } else { pX = SurfaceMap.GetEquirectangularXFromLonWithScale(longitude, precipitationMap.Width, precipitationScale, projection); } xToEX.Add(x, elevationX); xToWX.Add(x, wX); xToSX.Add(x, sX); xToPX.Add(x, pX); } var winterX = xToWX[x]; var summerX = xToSX[x]; var precipitationX = xToPX[x]; var normalizedElevation = elevationSpan[elevationX].GetValueFromPixel_PosNeg() - planet.NormalizedSeaLevel; totalElevation += normalizedElevation; var winterTemperature = (float)(winterSpan[winterX].GetValueFromPixel_Pos() * SurfaceMapImage.TemperatureScaleFactor); var summerTemperature = (float)(summerSpan[summerX].GetValueFromPixel_Pos() * SurfaceMapImage.TemperatureScaleFactor); minTemperature = Math.Min(minTemperature, Math.Min(winterTemperature, summerTemperature)); maxTemperature = Math.Max(maxTemperature, Math.Max(winterTemperature, summerTemperature)); totalTemperature += (minTemperature + maxTemperature) / 2; var precipValue = precipitationSpan[precipitationX].GetValueFromPixel_Pos(); var precipitation = precipValue * planet.Atmosphere.MaxPrecipitation; totalPrecipiation += precipValue; ClimateMap[x][y] = Universe.Climate.Climate.GetClimateType(new FloatRange( Math.Min(winterTemperature, summerTemperature), Math.Max(winterTemperature, summerTemperature))); humidityMap[x][y] = Universe.Climate.Climate.GetHumidityType(precipitation); BiomeMap[x][y] = Universe.Climate.Climate.GetBiomeType(ClimateMap[x][y], humidityMap[x][y], normalizedElevation); if (normalizedElevation > 0 || (summerTemperature >= Substances.All.Seawater.MeltingPoint && winterTemperature >= Substances.All.Seawater.MeltingPoint)) { continue; } if (summerTemperature < Substances.All.Seawater.MeltingPoint && winterTemperature < Substances.All.Seawater.MeltingPoint) { SeaIceRangeMap[x][y] = FloatRange.ZeroToOne; continue; } var freezeProportion = ((summerTemperature >= winterTemperature ? winterTemperature.InverseLerp(summerTemperature, (float)(Substances.All.Seawater.MeltingPoint ?? 0)) : summerTemperature.InverseLerp(winterTemperature, (float)(Substances.All.Seawater.MeltingPoint ?? 0))) * 0.8f) - 0.1f; if (freezeProportion <= 0 || float.IsNaN(freezeProportion)) { continue; } var freezeStart = 1 - (freezeProportion / 4); var iceMeltFinish = freezeProportion * 3 / 4; if (latitude < 0) { freezeStart += 0.5f; if (freezeStart > 1) { freezeStart--; } iceMeltFinish += 0.5f; if (iceMeltFinish > 1) { iceMeltFinish--; } } SeaIceRangeMap[x][y] = new FloatRange(freezeStart, iceMeltFinish); } } Climate = Universe.Climate.Climate.GetClimateType(new FloatRange(minTemperature, totalTemperature / (XLength * YLength), maxTemperature)); var humidity = Universe.Climate.Climate.GetHumidityType(totalPrecipiation / (XLength * YLength) * planet.Atmosphere.MaxPrecipitation); Biome = Universe.Climate.Climate.GetBiomeType(Climate, humidity, totalElevation / (XLength * YLength) * planet.MaxElevation); }