From 5b9b83ba0b99a26166193044d3e0e2a7870d2cdc Mon Sep 17 00:00:00 2001 From: tcl Date: Fri, 6 Nov 2015 17:01:44 +0100 Subject: [PATCH 1/3] Added some mountain levels and made satellite-map mountain-levels dynamic. Updated World.proto and World_pb2.py. --- worldengine/World.proto | 65 +++++----- worldengine/draw.py | 50 ++++---- worldengine/generation.py | 15 ++- worldengine/hdf5_serialization.py | 7 +- worldengine/protobuf/World_pb2.py | 165 ++++++++++++++----------- worldengine/simulations/temperature.py | 2 +- worldengine/world.py | 76 ++++++------ 7 files changed, 200 insertions(+), 180 deletions(-) diff --git a/worldengine/World.proto b/worldengine/World.proto index a65a82f4..692b0598 100644 --- a/worldengine/World.proto +++ b/worldengine/World.proto @@ -57,62 +57,65 @@ message World { required int32 height = 5; // Elevation - required DoubleMatrix heightMapData = 6; - required double heightMapTh_sea = 7; - required double heightMapTh_plain = 8; - required double heightMapTh_hill = 9; + required DoubleMatrix heightMapData = 6; + required double heightMapTh_sea = 7; + required double heightMapTh_plain = 8; + required double heightMapTh_hill = 9; + required double heightMapTh_low_mountain = 10; + required double heightMapTh_med_mountain = 11; + required double heightMapTh_high_mountain = 12; // Plates - required IntegerMatrix plates = 10; + required IntegerMatrix plates = 13; // Ocean - required BooleanMatrix ocean = 11; - required DoubleMatrix sea_depth = 12; + required BooleanMatrix ocean = 14; + required DoubleMatrix sea_depth = 15; // Biome - optional IntegerMatrix biome = 13; + optional IntegerMatrix biome = 16; // Humidity - optional DoubleMatrixWithQuantiles humidity = 14; + optional DoubleMatrixWithQuantiles humidity = 17; // Irrigation - optional DoubleMatrix irrigation = 15; + optional DoubleMatrix irrigation = 18; // Permeability - optional DoubleMatrix permeabilityData = 16; - optional double permeability_low = 17; - optional double permeability_med = 18; + optional DoubleMatrix permeabilityData = 19; + optional double permeability_low = 20; + optional double permeability_med = 21; // Watermap - optional DoubleMatrix watermapData = 19; - optional double watermap_creek = 20; - optional double watermap_river = 21; - optional double watermap_mainriver = 22; + optional DoubleMatrix watermapData = 22; + optional double watermap_creek = 23; + optional double watermap_river = 24; + optional double watermap_mainriver = 25; // Precipitation - optional DoubleMatrix precipitationData = 23; - optional double precipitation_low = 24; - optional double precipitation_med = 25; + optional DoubleMatrix precipitationData = 26; + optional double precipitation_low = 27; + optional double precipitation_med = 28; // Temperature - optional DoubleMatrix temperatureData = 26; - optional double temperature_polar = 27; - optional double temperature_alpine = 28; - optional double temperature_boreal = 29; - optional double temperature_cool = 30; - optional double temperature_warm = 31; - optional double temperature_subtropical = 32; + optional DoubleMatrix temperatureData = 29; + optional double temperature_polar = 30; + optional double temperature_alpine = 31; + optional double temperature_boreal = 32; + optional double temperature_cool = 33; + optional double temperature_warm = 34; + optional double temperature_subtropical = 35; // Data about generation: // introduced in v0.5.3 // this is optional for backward compatibility reasons - optional GenerationData generationData = 33; + optional GenerationData generationData = 36; - optional DoubleMatrix lakemap = 34; - optional DoubleMatrix rivermap = 35; + optional DoubleMatrix lakemap = 37; + optional DoubleMatrix rivermap = 38; // Ice-caps - optional DoubleMatrix icecap = 36; + optional DoubleMatrix icecap = 39; } diff --git a/worldengine/draw.py b/worldengine/draw.py index 663c0c1a..4e4ad626 100644 --- a/worldengine/draw.py +++ b/worldengine/draw.py @@ -11,13 +11,6 @@ ### For draw_satellite ### NOISE_RANGE = 15 # a random value between -NOISE_RANGE and NOISE_RANGE will be added to the rgb of each pixel -# These are arbitrarily-chosen elevation cutoffs for 4 different height levels. -# Some color modifiers will be applied at each level -HIGH_MOUNTAIN_ELEV = 215 -MOUNTAIN_ELEV = 175 -HIGH_HILL_ELEV = 160 -HILL_ELEV = 145 - # These are rgb color values which will be added to the noise, if the elevation is greater than the height specified # These are not cumulative HIGH_MOUNTAIN_NOISE_MODIFIER = (10, 6, 10) @@ -279,30 +272,29 @@ def get_biome_color_based_on_elevation(world, elev, x, y, rng): ## Generate some random noise to apply to this pixel # There is noise for each element of the rgb value # This noise will be further modified by the height of this tile - noise = rng.randint(-NOISE_RANGE, NOISE_RANGE, size=3) # draw three random numbers at once - ####### Case 1 - elevation is very high ######## - if elev > HIGH_MOUNTAIN_ELEV: - # Modify the noise to make the area slightly brighter to simulate snow-topped mountains. - noise = add_colors(noise, HIGH_MOUNTAIN_NOISE_MODIFIER) - # Average the biome's color with the MOUNTAIN_COLOR to tint the terrain - biome_color = average_colors(biome_color, MOUNTAIN_COLOR) - - ####### Case 2 - elevation is high ######## - elif elev > MOUNTAIN_ELEV: - # Modify the noise to make this tile slightly darker, especially draining the green - noise = add_colors(noise, MOUNTAIN_NOISE_MODIFIER) - # Average the biome's color with the MOUNTAIN_COLOR to tint the terrain - biome_color = average_colors(biome_color, MOUNTAIN_COLOR) - - ####### Case 3 - elevation is somewhat high ######## - elif elev > HIGH_HILL_ELEV: - noise = add_colors(noise, HIGH_HILL_NOISE_MODIFIER) - - ####### Case 4 - elevation is a little bit high ######## - elif elev > HILL_ELEV: - noise = add_colors(noise, HILL_NOISE_MODIFIER) + ####### Case 1 - elevation is very high ######## + if world.is_high_mountain((x, y)): + # Modify the noise to make the area slightly brighter to simulate snow-topped mountains. + noise = add_colors(noise, HIGH_MOUNTAIN_NOISE_MODIFIER) + # Average the biome's color with the MOUNTAIN_COLOR to tint the terrain + biome_color = average_colors(biome_color, MOUNTAIN_COLOR) + + ####### Case 2 - elevation is high ######## + elif world.is_medium_mountain((x, y)): + # Modify the noise to make this tile slightly darker, especially draining the green + noise = add_colors(noise, MOUNTAIN_NOISE_MODIFIER) + # Average the biome's color with the MOUNTAIN_COLOR to tint the terrain + biome_color = average_colors(biome_color, MOUNTAIN_COLOR) + + ####### Case 3 - elevation is somewhat high ######## + elif world.is_low_mountain((x, y)): + noise = add_colors(noise, HIGH_HILL_NOISE_MODIFIER) + + ####### Case 4 - elevation is a little bit high ######## + elif world.is_hill((x, y)): + noise = add_colors(noise, HILL_NOISE_MODIFIER) # There is also a minor base modifier to the pixel's rgb value based on height modification_amount = int(elev / BASE_ELEVATION_INTENSITY_MODIFIER) diff --git a/worldengine/generation.py b/worldengine/generation.py index 7854f662..7bf81ee8 100644 --- a/worldengine/generation.py +++ b/worldengine/generation.py @@ -105,12 +105,17 @@ def initialize_ocean_and_thresholds(world, ocean_level=1.0): """ e = world.elevation['data'] ocean = fill_ocean(e, ocean_level) - hl = find_threshold_f(e, 0.10) # the highest 10% of all (!) land are declared hills - ml = find_threshold_f(e, 0.03) # the highest 3% are declared mountains + pl = find_threshold_f(e, 0.70, ocean=ocean) # the highest x% of land are declared plains + hl = find_threshold_f(e, 0.35, ocean=ocean) # the highest x% are declared hills + ml = find_threshold_f(e, 0.10, ocean=ocean) # the highest x% are declared low mountains + mml = find_threshold_f(e, 0.06, ocean=ocean) # the highest x% are declared medium mountains + hml = find_threshold_f(e, 0.02, ocean=ocean) # the highest x% are declared high mountains e_th = [('sea', ocean_level), - ('plain', hl), - ('hill', ml), - ('mountain', None)] + ('plain', pl), + ('hill', hl), + ('mountain', ml), + ('med_mountain', mml), + ('high_mountain', hml)] harmonize_ocean(ocean, e, ocean_level) world.set_ocean(ocean) world.set_elevation(e, e_th) diff --git a/worldengine/hdf5_serialization.py b/worldengine/hdf5_serialization.py index daa0c392..8b2f542c 100644 --- a/worldengine/hdf5_serialization.py +++ b/worldengine/hdf5_serialization.py @@ -19,6 +19,9 @@ def save_world_to_hdf5(world, filename): elevation_ths_grp["sea"] = world.elevation['thresholds'][0][1] elevation_ths_grp["plain"] = world.elevation['thresholds'][1][1] elevation_ths_grp["hill"] = world.elevation['thresholds'][2][1] + elevation_ths_grp["low_mountain"] = world.elevation['thresholds'][3][1] + elevation_ths_grp["med_mountain"] = world.elevation['thresholds'][4][1] + elevation_ths_grp["high_mountain"] = world.elevation['thresholds'][5][1] elevation_data = elevation_grp.create_dataset("data", (world.height, world.width), dtype=numpy.float) elevation_data.write_direct(world.elevation['data']) @@ -137,7 +140,9 @@ def load_world_to_hdf5(filename): e_th = [('sea', f['elevation/thresholds/sea'].value), ('plain', f['elevation/thresholds/plain'].value), ('hill', f['elevation/thresholds/hill'].value), - ('mountain', None)] + ('mountain', f['elevation/thresholds/low_mountain'].value), + ('med_mountain', f['elevation/thresholds/med_mountain'].value), + ('high_mountain', f['elevation/thresholds/high_mountain'].value)] w.set_elevation(e, e_th) # Plates diff --git a/worldengine/protobuf/World_pb2.py b/worldengine/protobuf/World_pb2.py index c88bf5c0..4e0f5761 100644 --- a/worldengine/protobuf/World_pb2.py +++ b/worldengine/protobuf/World_pb2.py @@ -17,7 +17,7 @@ name='World.proto', package='World', syntax='proto2', - serialized_pb=b'\n\x0bWorld.proto\x12\x05World\"\xf6\r\n\x05World\x12\x17\n\x0fworldengine_tag\x18\x01 \x02(\x05\x12\x1b\n\x13worldengine_version\x18\x02 \x02(\x05\x12\x0c\n\x04name\x18\x03 \x02(\t\x12\r\n\x05width\x18\x04 \x02(\x05\x12\x0e\n\x06height\x18\x05 \x02(\x05\x12\x30\n\rheightMapData\x18\x06 \x02(\x0b\x32\x19.World.World.DoubleMatrix\x12\x17\n\x0fheightMapTh_sea\x18\x07 \x02(\x01\x12\x19\n\x11heightMapTh_plain\x18\x08 \x02(\x01\x12\x18\n\x10heightMapTh_hill\x18\t \x02(\x01\x12*\n\x06plates\x18\n \x02(\x0b\x32\x1a.World.World.IntegerMatrix\x12)\n\x05ocean\x18\x0b \x02(\x0b\x32\x1a.World.World.BooleanMatrix\x12,\n\tsea_depth\x18\x0c \x02(\x0b\x32\x19.World.World.DoubleMatrix\x12)\n\x05\x62iome\x18\r \x01(\x0b\x32\x1a.World.World.IntegerMatrix\x12\x38\n\x08humidity\x18\x0e \x01(\x0b\x32&.World.World.DoubleMatrixWithQuantiles\x12-\n\nirrigation\x18\x0f \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x33\n\x10permeabilityData\x18\x10 \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x18\n\x10permeability_low\x18\x11 \x01(\x01\x12\x18\n\x10permeability_med\x18\x12 \x01(\x01\x12/\n\x0cwatermapData\x18\x13 \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x16\n\x0ewatermap_creek\x18\x14 \x01(\x01\x12\x16\n\x0ewatermap_river\x18\x15 \x01(\x01\x12\x1a\n\x12watermap_mainriver\x18\x16 \x01(\x01\x12\x34\n\x11precipitationData\x18\x17 \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x19\n\x11precipitation_low\x18\x18 \x01(\x01\x12\x19\n\x11precipitation_med\x18\x19 \x01(\x01\x12\x32\n\x0ftemperatureData\x18\x1a \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x19\n\x11temperature_polar\x18\x1b \x01(\x01\x12\x1a\n\x12temperature_alpine\x18\x1c \x01(\x01\x12\x1a\n\x12temperature_boreal\x18\x1d \x01(\x01\x12\x18\n\x10temperature_cool\x18\x1e \x01(\x01\x12\x18\n\x10temperature_warm\x18\x1f \x01(\x01\x12\x1f\n\x17temperature_subtropical\x18 \x01(\x01\x12\x33\n\x0egenerationData\x18! \x01(\x0b\x32\x1b.World.World.GenerationData\x12*\n\x07lakemap\x18\" \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12+\n\x08rivermap\x18# \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12)\n\x06icecap\x18$ \x01(\x0b\x32\x19.World.World.DoubleMatrix\x1a\x1a\n\tDoubleRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x01\x1a\x1b\n\nBooleanRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x08\x1a\x1b\n\nIntegerRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x05\x1a\x18\n\x07\x42yteRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x05\x1a\x34\n\x0c\x44oubleMatrix\x12$\n\x04rows\x18\x01 \x03(\x0b\x32\x16.World.World.DoubleRow\x1a\x36\n\rBooleanMatrix\x12%\n\x04rows\x18\x01 \x03(\x0b\x32\x17.World.World.BooleanRow\x1a\x36\n\rIntegerMatrix\x12%\n\x04rows\x18\x01 \x03(\x0b\x32\x17.World.World.IntegerRow\x1a,\n\x0e\x44oubleQuantile\x12\x0b\n\x03key\x18\x01 \x02(\x05\x12\r\n\x05value\x18\x02 \x02(\x01\x1aq\n\x19\x44oubleMatrixWithQuantiles\x12.\n\tquantiles\x18\x01 \x03(\x0b\x32\x1b.World.World.DoubleQuantile\x12$\n\x04rows\x18\x02 \x03(\x0b\x32\x16.World.World.DoubleRow\x1aS\n\x0eGenerationData\x12\x0c\n\x04seed\x18\x01 \x01(\x05\x12\x10\n\x08n_plates\x18\x02 \x01(\x05\x12\x13\n\x0bocean_level\x18\x03 \x01(\x02\x12\x0c\n\x04step\x18\x04 \x01(\t' + serialized_pb=b'\n\x0bWorld.proto\x12\x05World\"\xdd\x0e\n\x05World\x12\x17\n\x0fworldengine_tag\x18\x01 \x02(\x05\x12\x1b\n\x13worldengine_version\x18\x02 \x02(\x05\x12\x0c\n\x04name\x18\x03 \x02(\t\x12\r\n\x05width\x18\x04 \x02(\x05\x12\x0e\n\x06height\x18\x05 \x02(\x05\x12\x30\n\rheightMapData\x18\x06 \x02(\x0b\x32\x19.World.World.DoubleMatrix\x12\x17\n\x0fheightMapTh_sea\x18\x07 \x02(\x01\x12\x19\n\x11heightMapTh_plain\x18\x08 \x02(\x01\x12\x18\n\x10heightMapTh_hill\x18\t \x02(\x01\x12 \n\x18heightMapTh_low_mountain\x18\n \x02(\x01\x12 \n\x18heightMapTh_med_mountain\x18\x0b \x02(\x01\x12!\n\x19heightMapTh_high_mountain\x18\x0c \x02(\x01\x12*\n\x06plates\x18\r \x02(\x0b\x32\x1a.World.World.IntegerMatrix\x12)\n\x05ocean\x18\x0e \x02(\x0b\x32\x1a.World.World.BooleanMatrix\x12,\n\tsea_depth\x18\x0f \x02(\x0b\x32\x19.World.World.DoubleMatrix\x12)\n\x05\x62iome\x18\x10 \x01(\x0b\x32\x1a.World.World.IntegerMatrix\x12\x38\n\x08humidity\x18\x11 \x01(\x0b\x32&.World.World.DoubleMatrixWithQuantiles\x12-\n\nirrigation\x18\x12 \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x33\n\x10permeabilityData\x18\x13 \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x18\n\x10permeability_low\x18\x14 \x01(\x01\x12\x18\n\x10permeability_med\x18\x15 \x01(\x01\x12/\n\x0cwatermapData\x18\x16 \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x16\n\x0ewatermap_creek\x18\x17 \x01(\x01\x12\x16\n\x0ewatermap_river\x18\x18 \x01(\x01\x12\x1a\n\x12watermap_mainriver\x18\x19 \x01(\x01\x12\x34\n\x11precipitationData\x18\x1a \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x19\n\x11precipitation_low\x18\x1b \x01(\x01\x12\x19\n\x11precipitation_med\x18\x1c \x01(\x01\x12\x32\n\x0ftemperatureData\x18\x1d \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12\x19\n\x11temperature_polar\x18\x1e \x01(\x01\x12\x1a\n\x12temperature_alpine\x18\x1f \x01(\x01\x12\x1a\n\x12temperature_boreal\x18 \x01(\x01\x12\x18\n\x10temperature_cool\x18! \x01(\x01\x12\x18\n\x10temperature_warm\x18\" \x01(\x01\x12\x1f\n\x17temperature_subtropical\x18# \x01(\x01\x12\x33\n\x0egenerationData\x18$ \x01(\x0b\x32\x1b.World.World.GenerationData\x12*\n\x07lakemap\x18% \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12+\n\x08rivermap\x18& \x01(\x0b\x32\x19.World.World.DoubleMatrix\x12)\n\x06icecap\x18\' \x01(\x0b\x32\x19.World.World.DoubleMatrix\x1a\x1a\n\tDoubleRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x01\x1a\x1b\n\nBooleanRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x08\x1a\x1b\n\nIntegerRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x05\x1a\x18\n\x07\x42yteRow\x12\r\n\x05\x63\x65lls\x18\x01 \x03(\x05\x1a\x34\n\x0c\x44oubleMatrix\x12$\n\x04rows\x18\x01 \x03(\x0b\x32\x16.World.World.DoubleRow\x1a\x36\n\rBooleanMatrix\x12%\n\x04rows\x18\x01 \x03(\x0b\x32\x17.World.World.BooleanRow\x1a\x36\n\rIntegerMatrix\x12%\n\x04rows\x18\x01 \x03(\x0b\x32\x17.World.World.IntegerRow\x1a,\n\x0e\x44oubleQuantile\x12\x0b\n\x03key\x18\x01 \x02(\x05\x12\r\n\x05value\x18\x02 \x02(\x01\x1aq\n\x19\x44oubleMatrixWithQuantiles\x12.\n\tquantiles\x18\x01 \x03(\x0b\x32\x1b.World.World.DoubleQuantile\x12$\n\x04rows\x18\x02 \x03(\x0b\x32\x16.World.World.DoubleRow\x1aS\n\x0eGenerationData\x12\x0c\n\x04seed\x18\x01 \x01(\x05\x12\x10\n\x08n_plates\x18\x02 \x01(\x05\x12\x13\n\x0bocean_level\x18\x03 \x01(\x02\x12\x0c\n\x04step\x18\x04 \x01(\t' ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -50,8 +50,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1283, - serialized_end=1309, + serialized_start=1386, + serialized_end=1412, ) _WORLD_BOOLEANROW = _descriptor.Descriptor( @@ -80,8 +80,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1311, - serialized_end=1338, + serialized_start=1414, + serialized_end=1441, ) _WORLD_INTEGERROW = _descriptor.Descriptor( @@ -110,8 +110,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1340, - serialized_end=1367, + serialized_start=1443, + serialized_end=1470, ) _WORLD_BYTEROW = _descriptor.Descriptor( @@ -140,8 +140,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1369, - serialized_end=1393, + serialized_start=1472, + serialized_end=1496, ) _WORLD_DOUBLEMATRIX = _descriptor.Descriptor( @@ -170,8 +170,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1395, - serialized_end=1447, + serialized_start=1498, + serialized_end=1550, ) _WORLD_BOOLEANMATRIX = _descriptor.Descriptor( @@ -200,8 +200,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1449, - serialized_end=1503, + serialized_start=1552, + serialized_end=1606, ) _WORLD_INTEGERMATRIX = _descriptor.Descriptor( @@ -230,8 +230,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1505, - serialized_end=1559, + serialized_start=1608, + serialized_end=1662, ) _WORLD_DOUBLEQUANTILE = _descriptor.Descriptor( @@ -267,8 +267,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1561, - serialized_end=1605, + serialized_start=1664, + serialized_end=1708, ) _WORLD_DOUBLEMATRIXWITHQUANTILES = _descriptor.Descriptor( @@ -304,8 +304,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1607, - serialized_end=1720, + serialized_start=1710, + serialized_end=1823, ) _WORLD_GENERATIONDATA = _descriptor.Descriptor( @@ -355,8 +355,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1722, - serialized_end=1805, + serialized_start=1825, + serialized_end=1908, ) _WORLD = _descriptor.Descriptor( @@ -430,190 +430,211 @@ is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='plates', full_name='World.World.plates', index=9, - number=10, type=11, cpp_type=10, label=2, - has_default_value=False, default_value=None, + name='heightMapTh_low_mountain', full_name='World.World.heightMapTh_low_mountain', index=9, + number=10, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='ocean', full_name='World.World.ocean', index=10, - number=11, type=11, cpp_type=10, label=2, - has_default_value=False, default_value=None, + name='heightMapTh_med_mountain', full_name='World.World.heightMapTh_med_mountain', index=10, + number=11, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='sea_depth', full_name='World.World.sea_depth', index=11, - number=12, type=11, cpp_type=10, label=2, - has_default_value=False, default_value=None, + name='heightMapTh_high_mountain', full_name='World.World.heightMapTh_high_mountain', index=11, + number=12, type=1, cpp_type=5, label=2, + has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='biome', full_name='World.World.biome', index=12, - number=13, type=11, cpp_type=10, label=1, + name='plates', full_name='World.World.plates', index=12, + number=13, type=11, cpp_type=10, label=2, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='humidity', full_name='World.World.humidity', index=13, - number=14, type=11, cpp_type=10, label=1, + name='ocean', full_name='World.World.ocean', index=13, + number=14, type=11, cpp_type=10, label=2, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='irrigation', full_name='World.World.irrigation', index=14, - number=15, type=11, cpp_type=10, label=1, + name='sea_depth', full_name='World.World.sea_depth', index=14, + number=15, type=11, cpp_type=10, label=2, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='permeabilityData', full_name='World.World.permeabilityData', index=15, + name='biome', full_name='World.World.biome', index=15, number=16, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='permeability_low', full_name='World.World.permeability_low', index=16, - number=17, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=0, + name='humidity', full_name='World.World.humidity', index=16, + number=17, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='permeability_med', full_name='World.World.permeability_med', index=17, - number=18, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=0, + name='irrigation', full_name='World.World.irrigation', index=17, + number=18, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='watermapData', full_name='World.World.watermapData', index=18, + name='permeabilityData', full_name='World.World.permeabilityData', index=18, number=19, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='watermap_creek', full_name='World.World.watermap_creek', index=19, + name='permeability_low', full_name='World.World.permeability_low', index=19, number=20, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='watermap_river', full_name='World.World.watermap_river', index=20, + name='permeability_med', full_name='World.World.permeability_med', index=20, number=21, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='watermap_mainriver', full_name='World.World.watermap_mainriver', index=21, - number=22, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=0, + name='watermapData', full_name='World.World.watermapData', index=21, + number=22, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='precipitationData', full_name='World.World.precipitationData', index=22, - number=23, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, + name='watermap_creek', full_name='World.World.watermap_creek', index=22, + number=23, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='precipitation_low', full_name='World.World.precipitation_low', index=23, + name='watermap_river', full_name='World.World.watermap_river', index=23, number=24, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='precipitation_med', full_name='World.World.precipitation_med', index=24, + name='watermap_mainriver', full_name='World.World.watermap_mainriver', index=24, number=25, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperatureData', full_name='World.World.temperatureData', index=25, + name='precipitationData', full_name='World.World.precipitationData', index=25, number=26, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperature_polar', full_name='World.World.temperature_polar', index=26, + name='precipitation_low', full_name='World.World.precipitation_low', index=26, number=27, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperature_alpine', full_name='World.World.temperature_alpine', index=27, + name='precipitation_med', full_name='World.World.precipitation_med', index=27, number=28, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperature_boreal', full_name='World.World.temperature_boreal', index=28, - number=29, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=0, + name='temperatureData', full_name='World.World.temperatureData', index=28, + number=29, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperature_cool', full_name='World.World.temperature_cool', index=29, + name='temperature_polar', full_name='World.World.temperature_polar', index=29, number=30, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperature_warm', full_name='World.World.temperature_warm', index=30, + name='temperature_alpine', full_name='World.World.temperature_alpine', index=30, number=31, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='temperature_subtropical', full_name='World.World.temperature_subtropical', index=31, + name='temperature_boreal', full_name='World.World.temperature_boreal', index=31, number=32, type=1, cpp_type=5, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='generationData', full_name='World.World.generationData', index=32, - number=33, type=11, cpp_type=10, label=1, + name='temperature_cool', full_name='World.World.temperature_cool', index=32, + number=33, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='temperature_warm', full_name='World.World.temperature_warm', index=33, + number=34, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='temperature_subtropical', full_name='World.World.temperature_subtropical', index=34, + number=35, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='generationData', full_name='World.World.generationData', index=35, + number=36, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='lakemap', full_name='World.World.lakemap', index=33, - number=34, type=11, cpp_type=10, label=1, + name='lakemap', full_name='World.World.lakemap', index=36, + number=37, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='rivermap', full_name='World.World.rivermap', index=34, - number=35, type=11, cpp_type=10, label=1, + name='rivermap', full_name='World.World.rivermap', index=37, + number=38, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( - name='icecap', full_name='World.World.icecap', index=35, - number=36, type=11, cpp_type=10, label=1, + name='icecap', full_name='World.World.icecap', index=38, + number=39, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, @@ -631,7 +652,7 @@ oneofs=[ ], serialized_start=23, - serialized_end=1805, + serialized_end=1908, ) _WORLD_DOUBLEROW.containing_type = _WORLD diff --git a/worldengine/simulations/temperature.py b/worldengine/simulations/temperature.py index 23acc20e..4b1b0ae8 100644 --- a/worldengine/simulations/temperature.py +++ b/worldengine/simulations/temperature.py @@ -11,7 +11,7 @@ def is_applicable(world): def execute(self, world, seed): e = world.elevation['data'] - ml = world.start_mountain_th() # returns how many percent of the world are mountains + ml = world.start_mountain_th() # returns minimum elevation value for mountains (i.e. hills) ocean = world.ocean t = self._calculate(world, seed, e, ml) diff --git a/worldengine/world.py b/worldengine/world.py index 9bcb20d6..d7bf88b7 100644 --- a/worldengine/world.py +++ b/worldengine/world.py @@ -168,6 +168,9 @@ def _to_protobuf_world(self): p_world.heightMapTh_sea = self.elevation['thresholds'][0][1] p_world.heightMapTh_plain = self.elevation['thresholds'][1][1] p_world.heightMapTh_hill = self.elevation['thresholds'][2][1] + p_world.heightMapTh_low_mountain = self.elevation['thresholds'][3][1] + p_world.heightMapTh_med_mountain = self.elevation['thresholds'][4][1] + p_world.heightMapTh_high_mountain = self.elevation['thresholds'][5][1] # Plates self._to_protobuf_matrix(self.plates, p_world.plates) @@ -244,7 +247,9 @@ def _from_protobuf_world(cls, p_world): e_th = [('sea', p_world.heightMapTh_sea), ('plain', p_world.heightMapTh_plain), ('hill', p_world.heightMapTh_hill), - ('mountain', None)] + ('mountain', p_world.heightMapTh_low_mountain), + ('med_mountain', p_world.heightMapTh_med_mountain), + ('high_mountain', p_world.heightMapTh_high_mountain)] w.set_elevation(e, e_th) # Plates @@ -416,66 +421,55 @@ def tiles_around_many(self, pos_list, radius=1, predicate=None): # def start_mountain_th(self): - return self.elevation['thresholds'][2][1] + return self.elevation['thresholds'][3][1] - def is_mountain(self, pos): + def is_mountain(self, pos): # general test if self.ocean[pos[1], pos[0]]: return False - if len(self.elevation['thresholds']) == 4: - mi = 2 - else: - mi = 1 - mountain_level = self.elevation['thresholds'][mi][1] + mountain_level = self.elevation['thresholds'][2][1] # hills are mountains, too + x, y = pos + return mountain_level <= self.elevation['data'][y, x] + + def is_hill(self, pos): + if self.ocean[pos[1], pos[0]]: + return False + hill_level = self.elevation['thresholds'][2][1] + low_mountain_level = self.elevation['thresholds'][3][1] x, y = pos - return self.elevation['data'][y][x] > mountain_level + return hill_level <= self.elevation['data'][y, x] < low_mountain_level def is_low_mountain(self, pos): - if not self.is_mountain(pos): + if self.ocean[pos[1], pos[0]]: return False - if len(self.elevation['thresholds']) == 4: - mi = 2 - else: - mi = 1 - mountain_level = self.elevation['thresholds'][mi][1] + low_mountain_level = self.elevation['thresholds'][3][1] + mountain_level = self.elevation['thresholds'][4][1] x, y = pos - return self.elevation['data'][y, x] < mountain_level + 2.0 + return low_mountain_level <= self.elevation['data'][y, x] < mountain_level - def level_of_mountain(self, pos): + def is_medium_mountain(self, pos): if self.ocean[pos[1], pos[0]]: return False - if len(self.elevation['thresholds']) == 4: - mi = 2 - else: - mi = 1 - mountain_level = self.elevation['thresholds'][mi][1] + mountain_level = self.elevation['thresholds'][4][1] + high_mountain_level = self.elevation['thresholds'][5][1] x, y = pos - if self.elevation['data'][y, x] <= mountain_level: - return 0 - else: - return self.elevation['data'][y, x] - mountain_level + return mountain_level <= self.elevation['data'][y, x] < high_mountain_level def is_high_mountain(self, pos): - if not self.is_mountain(pos): + if self.ocean[pos[1], pos[0]]: return False - if len(self.elevation['thresholds']) == 4: - mi = 2 - else: - mi = 1 - mountain_level = self.elevation['thresholds'][mi][1] + high_mountain_level = self.elevation['thresholds'][5][1] x, y = pos - return self.elevation['data'][y, x] > mountain_level + 4.0 + return high_mountain_level <= self.elevation['data'][y, x] - def is_hill(self, pos): + def level_of_mountain(self, pos): if self.ocean[pos[1], pos[0]]: return False - if len(self.elevation['thresholds']) == 4: - hi = 1 - else: - hi = 0 - hill_level = self.elevation['thresholds'][hi][1] - mountain_level = self.elevation['thresholds'][hi + 1][1] + mountain_level = self.elevation['thresholds'][3][1] x, y = pos - return hill_level < self.elevation['data'][y, x] < mountain_level + if self.elevation['data'][y, x] < mountain_level: + return 0 + else: + return self.elevation['data'][y, x] - mountain_level def elevation_at(self, pos): return self.elevation['data'][pos[1], pos[0]] From 4b4a258174f1c774937c2421aec74ec76d6e6cc4 Mon Sep 17 00:00:00 2001 From: tcl Date: Wed, 18 Nov 2015 19:08:24 +0100 Subject: [PATCH 2/3] Refined mountain-levels. Altitude-factor for temperature-calculation makes use of the new levels. River-generation makes use of the new levels. Ancient map-generation makes use of the new levels. --- worldengine/drawing_functions.py | 4 ++-- worldengine/generation.py | 13 ++++++---- worldengine/simulations/erosion.py | 4 +--- worldengine/simulations/temperature.py | 22 ++++++++--------- worldengine/world.py | 33 +++++++++++++++----------- 5 files changed, 40 insertions(+), 36 deletions(-) diff --git a/worldengine/drawing_functions.py b/worldengine/drawing_functions.py index b6acd34a..491bfc12 100644 --- a/worldengine/drawing_functions.py +++ b/worldengine/drawing_functions.py @@ -100,10 +100,10 @@ def _find_mountains_mask(world, factor): _mask = numpy.zeros((factor * world.height, factor * world.width), dtype=float) for y in range(factor * world.height): for x in range(factor * world.width): - if world.is_mountain((int(x / factor), int(y / factor))): + if world.is_low_mountain((int(x / factor), int(y / factor)), include_higher=True): # maybe use is_medium_mountain()? v = len(world.tiles_around((int(x / factor), int(y / factor)), radius=3, - predicate=world.is_mountain)) + predicate=lambda pos: world.is_low_mountain(pos, include_higher=True))) if v > 32: _mask[y, x] = v / 4.0 # force conversion to float, Python 2 will *not* do it automatically return _mask diff --git a/worldengine/generation.py b/worldengine/generation.py index 7bf81ee8..8d0ba942 100644 --- a/worldengine/generation.py +++ b/worldengine/generation.py @@ -105,11 +105,14 @@ def initialize_ocean_and_thresholds(world, ocean_level=1.0): """ e = world.elevation['data'] ocean = fill_ocean(e, ocean_level) - pl = find_threshold_f(e, 0.70, ocean=ocean) # the highest x% of land are declared plains - hl = find_threshold_f(e, 0.35, ocean=ocean) # the highest x% are declared hills - ml = find_threshold_f(e, 0.10, ocean=ocean) # the highest x% are declared low mountains - mml = find_threshold_f(e, 0.06, ocean=ocean) # the highest x% are declared medium mountains - hml = find_threshold_f(e, 0.02, ocean=ocean) # the highest x% are declared high mountains + + # values chosen to emulate the exponential behavior seen here: + # http://www.ngdc.noaa.gov/mgg/global/etopo1_surface_histogram.html + pl = find_threshold_f(e, 0.95, ocean=ocean) # the highest x% of land are declared plains; save some for beaches (?) + hl = find_threshold_f(e, 0.50, ocean=ocean) # the highest x% are declared hills + ml = find_threshold_f(e, 0.25, ocean=ocean) # the highest x% are declared low mountains + mml = find_threshold_f(e, 0.12, ocean=ocean) # the highest x% are declared medium mountains + hml = find_threshold_f(e, 0.06, ocean=ocean) # the highest x% are declared high mountains e_th = [('sea', ocean_level), ('plain', pl), ('hill', hl), diff --git a/worldengine/simulations/erosion.py b/worldengine/simulations/erosion.py index 65b93847..9eb6bc24 100644 --- a/worldengine/simulations/erosion.py +++ b/worldengine/simulations/erosion.py @@ -152,9 +152,7 @@ def river_sources(world, water_flow, water_path): while not neighbour_seed_found: # have we found a seed? - if world.is_mountain((cx, cy)) and \ - water_flow[cy, cx] >= RIVER_TH: - + if world.is_low_mountain((cx, cy), include_higher=True) and water_flow[cy, cx] >= RIVER_TH: # try not to create seeds around other seeds for seed in river_source_list: sx, sy = seed diff --git a/worldengine/simulations/temperature.py b/worldengine/simulations/temperature.py index 4b1b0ae8..7b902e15 100644 --- a/worldengine/simulations/temperature.py +++ b/worldengine/simulations/temperature.py @@ -10,11 +10,9 @@ def is_applicable(world): return not world.has_temperature() def execute(self, world, seed): - e = world.elevation['data'] - ml = world.start_mountain_th() # returns minimum elevation value for mountains (i.e. hills) ocean = world.ocean - t = self._calculate(world, seed, e, ml) + t = self._calculate(world, seed) t_th = [ ('polar', find_threshold_f(t, world.temps[0], ocean)), ('alpine', find_threshold_f(t, world.temps[1], ocean)), @@ -27,13 +25,18 @@ def execute(self, world, seed): world.set_temperature(t, t_th) @staticmethod - def _calculate(world, seed, elevation, mountain_level): + def _calculate(world, seed): width = world.width height = world.height + e = world.elevation['data'] + mountain_level = world.start_mountain_th() # returns minimum elevation value for mountains (i.e. hills) + highest_mountain_level = e.max() # world.level_of_mountain(numpy.unravel_index(e.argmax(), e.shape)) # height of the highest point relative to mountain_level rng = numpy.random.RandomState(seed) # create our own random generator base = rng.randint(0, 4096) temp = numpy.zeros((height, width), dtype=float) + + min_altitude_factor = 0.3 # used for the top of the highest mountain; must be in [0, 1] ''' Set up variables to take care of some orbital parameters: @@ -89,13 +92,8 @@ def _calculate(world, seed, elevation, mountain_level): base=base) * (border - x) / border) t = (latitude_factor * 12 + n * 1) / 13.0 / distance_to_sun - if elevation[y, x] > mountain_level: # vary temperature based on height - if elevation[y, x] > (mountain_level + 29): - altitude_factor = 0.033 - else: - altitude_factor = 1.00 - ( - float(elevation[y, x] - mountain_level) / 30) - t *= altitude_factor - temp[y, x] = t + + # vary temperature based on height + temp[y, x] = t * numpy.interp(e[y, x], [mountain_level, highest_mountain_level], [1.00, min_altitude_factor]) return temp diff --git a/worldengine/world.py b/worldengine/world.py index d7bf88b7..708b142d 100644 --- a/worldengine/world.py +++ b/worldengine/world.py @@ -423,42 +423,47 @@ def tiles_around_many(self, pos_list, radius=1, predicate=None): def start_mountain_th(self): return self.elevation['thresholds'][3][1] - def is_mountain(self, pos): # general test - if self.ocean[pos[1], pos[0]]: - return False - mountain_level = self.elevation['thresholds'][2][1] # hills are mountains, too - x, y = pos - return mountain_level <= self.elevation['data'][y, x] - - def is_hill(self, pos): + def is_hill(self, pos, include_higher=False): if self.ocean[pos[1], pos[0]]: return False hill_level = self.elevation['thresholds'][2][1] low_mountain_level = self.elevation['thresholds'][3][1] x, y = pos - return hill_level <= self.elevation['data'][y, x] < low_mountain_level + if include_higher: + return hill_level <= self.elevation['data'][y, x] + else: + return hill_level <= self.elevation['data'][y, x] < low_mountain_level - def is_low_mountain(self, pos): + def is_low_mountain(self, pos, include_higher=False): if self.ocean[pos[1], pos[0]]: return False low_mountain_level = self.elevation['thresholds'][3][1] mountain_level = self.elevation['thresholds'][4][1] x, y = pos - return low_mountain_level <= self.elevation['data'][y, x] < mountain_level + if include_higher: + return low_mountain_level <= self.elevation['data'][y, x] + else: + return low_mountain_level <= self.elevation['data'][y, x] < mountain_level - def is_medium_mountain(self, pos): + def is_medium_mountain(self, pos, include_higher=False): if self.ocean[pos[1], pos[0]]: return False mountain_level = self.elevation['thresholds'][4][1] high_mountain_level = self.elevation['thresholds'][5][1] x, y = pos - return mountain_level <= self.elevation['data'][y, x] < high_mountain_level + if include_higher: + return mountain_level <= self.elevation['data'][y, x] + else: + return mountain_level <= self.elevation['data'][y, x] < high_mountain_level - def is_high_mountain(self, pos): + def is_high_mountain(self, pos, include_higher=False): + # include_higher is not used here (there is no higher threshold anyway); it is there to give all functions of this type the same signature if self.ocean[pos[1], pos[0]]: return False high_mountain_level = self.elevation['thresholds'][5][1] x, y = pos + if include_higher: + pass return high_mountain_level <= self.elevation['data'][y, x] def level_of_mountain(self, pos): From a3d048f777dbdd6db611829ece6e5b13a07dd5a1 Mon Sep 17 00:00:00 2001 From: tcl Date: Fri, 20 Nov 2015 13:11:14 +0100 Subject: [PATCH 3/3] Slightly modified the mountain-levels again. --- worldengine/generation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/worldengine/generation.py b/worldengine/generation.py index 8d0ba942..3b86295f 100644 --- a/worldengine/generation.py +++ b/worldengine/generation.py @@ -108,11 +108,11 @@ def initialize_ocean_and_thresholds(world, ocean_level=1.0): # values chosen to emulate the exponential behavior seen here: # http://www.ngdc.noaa.gov/mgg/global/etopo1_surface_histogram.html - pl = find_threshold_f(e, 0.95, ocean=ocean) # the highest x% of land are declared plains; save some for beaches (?) - hl = find_threshold_f(e, 0.50, ocean=ocean) # the highest x% are declared hills - ml = find_threshold_f(e, 0.25, ocean=ocean) # the highest x% are declared low mountains - mml = find_threshold_f(e, 0.12, ocean=ocean) # the highest x% are declared medium mountains - hml = find_threshold_f(e, 0.06, ocean=ocean) # the highest x% are declared high mountains + pl = find_threshold_f(e, 0.80, ocean=ocean) # the highest x% of land are declared plains; save some for beaches (?) + hl = find_threshold_f(e, 0.40, ocean=ocean) # the highest x% are declared hills + ml = find_threshold_f(e, 0.20, ocean=ocean) # the highest x% are declared low mountains + mml = find_threshold_f(e, 0.10, ocean=ocean) # the highest x% are declared medium mountains + hml = find_threshold_f(e, 0.05, ocean=ocean) # the highest x% are declared high mountains e_th = [('sea', ocean_level), ('plain', pl), ('hill', hl),