parameters.py 27 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
"""
3
Central place to store parameters / settings / variables in osm2city.
4 5 6
All length, height etc. parameters are in meters, square meters (m2) etc.

The assigned values are default values. The Config files will overwrite them
7

8 9 10
Created on May 27, 2013

@author: vanosten
11 12

Ludomotico contributed a cleaner version of read_from_file().
13
"""
14

15
import argparse
16
import logging
17
import sys
18
import traceback
19
import types
20 21
import typing
import unittest
22

23
import textures.road
24
import utils.vec2d as v
25
import utils.calc_tile as ct
26
import utils.log_helper as ulog
27

Thomas Albrecht's avatar
Thomas Albrecht committed
28
# default_args_start # DO NOT MODIFY THIS LINE
29
# -*- coding: utf-8 -*-
30
# The preceding line sets encoding of this file to utf-8. Needed for non-ascii
31 32
# object names. It must stay on the first or second line.

33
# =============================================================================
34
# PARAMETERS FOR ALL osm2city MODULES
35
# =============================================================================
Thomas Albrecht's avatar
Thomas Albrecht committed
36
# -- Scenery folder, typically a geographic name or the ICAO code of the airport
37 38
PREFIX = "LSZR"

39
# -- Boundary of the scenery in degrees (use "." not ","). The example below is from LSZR.
40
# The values are set dynamically during program execution - no need to set them manually.
41 42 43 44 45
BOUNDARY_WEST = 9.54
BOUNDARY_SOUTH = 47.48
BOUNDARY_EAST = 9.58
BOUNDARY_NORTH = 47.50

Thomas Albrecht's avatar
Thomas Albrecht committed
46 47 48 49 50
# -- Full path to the scenery folder without trailing slash. This is where we
#    will probe elevation and check for overlap with static objects. Most
#    likely you'll want to use your TerraSync path here.
PATH_TO_SCENERY = "/home/user/fgfs/scenery/TerraSync"

51
# Optional additional list of paths to scenery folders (e.g. project3000).
52
# Only used for overlap checking for buildings against static and shared objects
53
PATH_TO_SCENERY_OPT = None  # if not none, then needs to be list of strings
54

Thomas Albrecht's avatar
Thomas Albrecht committed
55 56 57 58 59 60
# -- The generated scenery (.stg, .ac, .xml) will be written to this path.
#    If empty, we'll use the correct location in PATH_TO_SCENERY. Note that
#    if you use TerraSync for PATH_TO_SCENERY, you MUST choose a different
#    path here. Otherwise, TerraSync will overwrite the generated scenery.
#    Also make sure PATH_TO_OUTPUT is included in your $FG_SCENERY.
PATH_TO_OUTPUT = "/home/user/fgfs/scenery/osm2city"
61

62
PATH_TO_OSM2CITY_DATA = "/home/user/osm2city-data"
63 64 65 66 67
DB_HOST = "localhost"  # The host name of the computer running PostGIS.
DB_PORT = 5432  # The port used to connect to the host
DB_NAME = "osmgis"  # The name of the database.
DB_USER = "gisuser"  # The name of the user to be used to read from the database.
DB_USER_PASSWORD = "n/a"  # The password for the DB_USER.
68

69 70
NO_ELEV = False             # -- skip elevation probing
FG_ELEV = '"D:/Program Files/FlightGear/bin/Win64/fgelev.exe"'
71
FG_ELEV_CACHE = True  # saves the elevation probing results to a file, so next rerun is faster (but uses disk space!)
72
PROBE_FOR_WATER = True  # only possible with FGElev version after 9th of November 2016 / FG 2016.4.1
73

74 75
TILE_SIZE = 2000            # -- tile size in meters for clustering of buildings, roads, ...

76 77
WRITE_CLUSTER_STATS = False

78
FLAG_COLOUR_TEX = False  # Feature flag for using colour codes in textures and OSM tags
79
FLAG_STG_BUILDING_LIST = False  # use BUILDING_LIST in stg-files
80 81
FLAG_BUILDINGS_LIST_SKIP = False
FLAG_BUILDINGS_MESH_SKIP = False
82

83 84 85 86 87 88 89
# Debugging by plotting with Matplotlib to pdfs. See description about its use in the appendix of the manual
DEBUG_PLOT_RECTIFY = False
DEBUG_PLOT_GENBUILDINGS = False
DEBUG_PLOT_LANDUSE = False
DEBUG_PLOT_ROADS = False
DEBUG_PLOT_OFFSETS = False

90
# =============================================================================
91
# PARAMETERS RELATED TO BUILDINGS IN osm2city
92
# =============================================================================
93

94
# -- Check for static objects in the PATH_TO_SCENERY folder based on convex hull around all points
95
OVERLAP_CHECK_CONVEX_HULL = True
96 97
OVERLAP_CHECK_CH_BUFFER_STATIC = 0.0
OVERLAP_CHECK_CH_BUFFER_SHARED = 0.0
98 99

OVERLAP_CHECK_CONSIDER_SHARED = True
100

101
# -- Skip buildings based on their OSM name tag or OSM ID, e.g. in case there's already
102
#    a static model for these, and the overlap check fails.
103
#    Use unicode strings as in the first example if there are non-ASCII characters.
104
#    E.g. SKIP_LIST = ["Theologische Fakultät", "Rhombergpassage", 55875208]
105
#    For roads/railways OSM ID is checked.
106
SKIP_LIST = []
107
SKIP_LIST_OVERLAP = []  # list of .ac or .xml file names which should not be used for overlap tests
108

109
# -- Parameters which influence the number of buildings from OSM taken to output
110 111
BUILDING_MIN_HEIGHT = 0.0           # -- minimum height from bottom to top without roof height of a building to be included in output (does not include roof). Different from OSM tag "min_height", which states that the bottom of the building hovers min_height over the ground
# If set to 0.0, then not taken into consideration (default)
112 113
BUILDING_MIN_AREA = 50.0            # -- minimum area for a building to be included in output (not used for buildings with parent)
BUILDING_PART_MIN_AREA = 10.0  # minimum area for building:parts
114 115
BUILDING_REDUCE_THRESHOLD = 200.0   # -- threshold area of a building below which a rate of buildings gets reduced from output
BUILDING_REDUCE_RATE = 0.5          # -- rate (between 0 and 1) of buildings below a threshold which get reduced randomly in output
116
BUILDING_REDUCE_CHECK_TOUCH = False # -- before removing a building due to area, check whether it is touching another building and therefore should be kept
117
BUILDING_NEVER_SKIP_LEVELS = 6      # -- buildings that tall will never be skipped
118 119
BUILDING_SIMPLIFY_TOLERANCE_LINE = 1.0
BUILDING_SIMPLIFY_TOLERANCE_AWAY = 2.5
120

121
BUILDING_COMPLEX_ROOFS = True       # -- generate complex roofs on buildings? I.e. other shapes than horizontal and flat
122
BUILDING_COMPLEX_ROOFS_MIN_LEVELS = 1  # don't put complex roof on buildings smaller than the specified value unless there is an explicit roof:shape flag
123
BUILDING_COMPLEX_ROOFS_MAX_LEVELS = 5   # don't put complex roofs on buildings taller than the specified value unless there is an explicit roof:shape flag
124 125
BUILDING_COMPLEX_ROOFS_MAX_AREA = 1600  # -- don't put complex roofs on buildings larger than this
BUILDING_COMPLEX_ROOFS_MIN_RATIO_AREA = 600  # if larger than this then ratio of length vs. area must be fulfilled
126
BUILDING_SKEL_ROOFS_MIN_ANGLE = 10  # -- pySkeleton based complex roofs will
127
BUILDING_SKEL_ROOFS_MAX_ANGLE = 50  #    have a random angle between MIN and MAX
128
BUILDING_SKEL_MAX_NODES = 10        # -- max number of nodes for which we generate pySkeleton roofs
129 130
BUILDING_SKILLION_ROOF_MAX_HEIGHT = 2.
BUILDING_SKEL_ROOF_MAX_HEIGHT = 6.  # -- skip skeleton roofs (gabled, pyramidal, ..) if the roof height is larger than this
131
BUILDING_ROOF_SIMPLIFY_TOLERANCE = .5
132 133

# If the roof_type is missing, what shall be the distribution of roof_types (must sum up to 1.0)
134
# The keys are the shapes and must correspond to valid RoofShape values in roofs.py
135
BUILDING_ROOF_SHAPE_RATIO = {'flat': 0.1, 'gabled': 0.8, 'hipped': 0.1}
136

137
# ==================== RECTIFY BUILDINGS ============
138
RECTIFY_ENABLED = True
139 140 141 142 143
RECTIFY_MAX_DRAW_SAMPLE = 20
RECTIFY_SEED_SAMPLE = True
RECTIFY_MAX_90_DEVIATION = 7
RECTIFY_90_TOLERANCE = 0.1

144 145 146
# Force European style inner cities with gables and red tiles
BUILDING_FORCE_EUROPEAN_INNER_CITY_STYLE = False

147
BUILDING_FAKE_AMBIENT_OCCLUSION = True      # -- fake AO by darkening facade textures towards the ground, using
148
BUILDING_FAKE_AMBIENT_OCCLUSION_HEIGHT = 6.  # 1 - VALUE * exp(- AGL / HEIGHT )
149
BUILDING_FAKE_AMBIENT_OCCLUSION_VALUE = 0.6
150

151
# Parameters which influence the height of buildings if no info from OSM is available.
152 153
BUILDING_NUMBER_LEVELS_CENTRE = {4: 0.2, 5: 0.7, 6: 0.1}
BUILDING_NUMBER_LEVELS_BLOCK = {4: 0.4, 5: 0.6}
154
BUILDING_NUMBER_LEVELS_DENSE = {3: 0.25, 4: 0.75}
155 156
BUILDING_NUMBER_LEVELS_PERIPHERY = {1: 0.3, 2: 0.65, 3: 0.05}
BUILDING_NUMBER_LEVELS_RURAL = {1: 0.3, 2: 0.7}
157 158 159 160 161
# the following are used if settlement type is not centre or block and building class is not residential
BUILDING_NUMBER_LEVELS_APARTMENTS = {2: 0.05, 3: 0.45, 4: 0.4, 5: 0.08, 6: 0.02}
BUILDING_NUMBER_LEVELS_INDUSTRIAL = {1: 0.3, 2: 0.6, 3: 0.1}  # for both industrial and warehouse
BUILDING_NUMBER_LEVELS_OTHER = {1: 0.2, 2: 0.4, 3: 0.3, 4: 0.1}  # e.g. commercial, public, retail

162
BUILDING_LEVEL_HEIGHT_URBAN = 3.5  # this value should not be changed unless special textures are used
163
BUILDING_LEVEL_HEIGHT_RURAL = 2.5  # ditto including periphery
164
BUILDING_LEVEL_HEIGHT_INDUSTRIAL = 6.  # for industrial and warehouse
165

166 167 168 169 170 171
# make sure not visible buildings or building parts are excluded
BUILDING_UNDERGROUND_LOCATION = True
BUILDING_UNDERGROUND_INDOOR = True
BUILDING_UNDERGROUND_TUNNEL = True
BUILDING_UNDERGROUND_LEVEL_NEGATIVE = True

172 173
BUILDING_USE_SHARED_WORSHIP = False  # try to use shared models for worship buildings

174 175 176 177
# a hex value for the colour to be used if the colour value in OSM is missing or cannot be interpreted
BUILDING_FACADE_DEFAULT_COLOUR = '#D3D3D3'  # e.g. #d3d3d3 - light grey
BUILDING_ROOF_DEFAULT_COLOUR = '#B22222'  # e.g. #b22222 - firebrick

178 179 180
BUILDING_NUMBER_LEVELS_AEROWAY = 2
BUILDING_LEVEL_HEIGHT_AEROWAY = 3.5

181
# -- The more buildings end up in LOD rough, the more work for your GPU.
182 183 184 185 186 187 188
#    Increasing any of the following parameters will decrease GPU load.
LOD_ALWAYS_DETAIL_BELOW_AREA = 150  # -- below this area, buildings will always be LOD detail
LOD_ALWAYS_ROUGH_ABOVE_AREA = 500   # -- above this area, buildings will always be LOD rough
LOD_ALWAYS_ROUGH_ABOVE_LEVELS = 6   # -- above this number of levels, buildings will always be LOD rough
LOD_ALWAYS_DETAIL_BELOW_LEVELS = 3  # -- below this number of levels, buildings will always be LOD detail
LOD_PERCENTAGE_DETAIL = 0.5         # -- of the remaining buildings, this percentage will be LOD detail,
                                    #    the rest will be LOD rough.
189

190
OBSTRUCTION_LIGHT_MIN_LEVELS = 15   # -- put obstruction lights on buildings with >= given levels. 0 for no lights.
191

192 193
# discard cluster if too few objects. Do not go below 1, otherwise lots of empty ac-objects and useless STG entries.
CLUSTER_MIN_OBJECTS = 5
194

195 196 197
# When searching for an existing OSM node based on distance: what is the allowed tolerance.
# Also used in roads.py to make sure blocked areas do not generate tiny differences.
TOLERANCE_MATCH_NODE = 0.5
198

199 200
DETAILS_PROCESS_PIERS = True
DETAILS_PROCESS_PLATFORMS = True
201

202
# =============================================================================
203
# PARAMETERS RELATED TO PYLONS, POWERLINES, AERIALWAYS IN pylons.py
204
# =============================================================================
205

206
C2P_PROCESS_POWERLINES = True
207
C2P_PROCESS_POWERLINES_MINOR = False  # only considered if C2P_PROCESS_POWERLINES is True
208
C2P_PROCESS_AERIALWAYS = False
209
C2P_PROCESS_OVERHEAD_LINES = False
210
C2P_PROCESS_WIND_TURBINES = True
211
C2P_PROCESS_STREETLAMPS = False  # experimental and unsupported
212
C2P_PROCESS_STORAGE_TANKS = True
213
C2P_PROCESS_CHIMNEYS = True
214

215 216
# The radius for the cable. The cable will be a triangle with side length 2*radius.
# In order to be better visible the radius might be chosen larger than in real life
217
C2P_RADIUS_POWER_LINE = 0.1
218 219 220 221
C2P_RADIUS_POWER_MINOR_LINE = 0.05
C2P_RADIUS_AERIALWAY_CABLE_CAR = 0.05
C2P_RADIUS_AERIALWAY_CHAIR_LIFT = 0.05
C2P_RADIUS_AERIALWAY_DRAG_LIFT = 0.03
222
C2P_RADIUS_AERIALWAY_GONDOLA = 0.05
223 224 225
C2P_RADIUS_AERIALWAY_GOODS = 0.03
C2P_RADIUS_TOP_LINE = 0.02
C2P_RADIUS_OVERHEAD_LINE = 0.02
226 227 228 229 230

# The number of extra points between 2 pylons to simulate sagging of the cable.
# If 0 is chosen or if CATENARY_A is 0 then no sagging is calculated, which is better for performances (less realistic)
# 3 is normally a good compromise - for cable cars or major power lines with very long distances a value of 5
# or higher might be suitable
231 232 233 234 235 236 237
C2P_EXTRA_VERTICES_POWER_LINE = 3
C2P_EXTRA_VERTICES_POWER_MINOR_LINE = 3
C2P_EXTRA_VERTICES_AERIALWAY_CABLE_CAR = 5
C2P_EXTRA_VERTICES_AERIALWAY_CHAIR_LIFT = 3
C2P_EXTRA_VERTICES_AERIALWAY_DRAG_LIFT = 0
C2P_EXTRA_VERTICES_AERIALWAY_GONDOLA = 3
C2P_EXTRA_VERTICES_AERIALWAY_GOODS = 5
238
C2P_EXTRA_VERTICES_OVERHEAD_LINE = 2
239 240

# The value for catenary_a can be experimentally determined by using osm2pylon.test_catenary
241
C2P_CATENARY_A_POWER_LINE = 1500
242
C2P_CATENARY_A_POWER_MINOR_LINE = 1200
243 244 245 246 247
C2P_CATENARY_A_AERIALWAY_CABLE_CAR = 1500
C2P_CATENARY_A_AERIALWAY_CHAIR_LIFT = 1500
C2P_CATENARY_A_AERIALWAY_DRAG_LIFT = 1500
C2P_CATENARY_A_AERIALWAY_GONDOLA = 1500
C2P_CATENARY_A_AERIALWAY_GOODS = 1500
248
C2P_CATENARY_A_OVERHEAD_LINE = 600
249
C2P_CATENARY_A_MAX_SAGGING = 0.3  # the maximum sagging allowed no matter the catenary a realtive to lowest cable height
250

251
C2P_CATENARY_MIN_DISTANCE = 30
252 253

C2P_POWER_LINE_ALLOW_100M = False
254

255
C2P_STREETLAMPS_MAX_DISTANCE_LANDUSE = 100
256 257
C2P_STREETLAMPS_RESIDENTIAL_DISTANCE = 40
C2P_STREETLAMPS_OTHER_DISTANCE = 70
258
C2P_STREETLAMPS_MIN_STREET_LENGTH = 20
259

260 261
C2P_WIND_TURBINE_MAX_DISTANCE_WITHIN_WIND_FARM = 700
C2P_WIND_TURBINE_MIN_DISTANCE_SHARED_OBJECT = 10
262

263
C2P_CHIMNEY_BRICK_RATION = 0.2  # the ratio of chimneys being made of bricks (rest is cement etc.)
264
C2P_CHIMNEY_MIN_HEIGHT = 30  # the minimum height a Chimney needs to have to be taken into account. Depends on available static models
265 266 267
C2P_CHIMNEY_DEFAULT_HEIGHT = 100  # the default height of chimneys, where the height is not specified in OSM
C2P_CHIMNEY_DEFAULT_HEIGHT_VARIATION = 20  # a random variation on top of the default height between 0 and value

268
# =============================================================================
Thomas Albrecht's avatar
Thomas Albrecht committed
269
# PARAMETERS RELATED TO roads.py
270
# =============================================================================
271

Thomas Albrecht's avatar
Thomas Albrecht committed
272
MAX_SLOPE_RAILWAY = 0.04
Thomas Albrecht's avatar
Thomas Albrecht committed
273
MAX_SLOPE_MOTORWAY = 0.03       # max slope for motorways
274
MAX_SLOPE_ROAD = 0.08
275
MAX_TRANSVERSE_GRADIENT = 0.1   #
Thomas Albrecht's avatar
Thomas Albrecht committed
276
BRIDGE_MIN_LENGTH = 20.         # discard short bridges, draw road instead
Thomas Albrecht's avatar
Thomas Albrecht committed
277
CREATE_BRIDGES_ONLY = 0         # create only bridges and embankments
278 279
BRIDGE_LAYER_HEIGHT = 4.         # bridge height per layer
BRIDGE_BODY_HEIGHT = 0.9         # height of bridge body
280
EMBANKMENT_TEXTURE = textures.road.EMBANKMENT_1  # Texture for the embankment
281 282
MIN_ABOVE_GROUND_LEVEL = 0.03    # how much a highway / railway is at least hovering above ground
DISTANCE_BETWEEN_LAYERS = 0.05  # ow much different layers of roads/railways at the same node are separated
283
HIGHWAY_TYPE_MIN = 4  # The lower the number, the more ways are added. See roads.HighwayType
284
HIGHWAY_TYPE_MIN_ROUGH_LOD = 6  # the minimum type tobe added to the rough LOD clusters
285 286
POINTS_ON_LINE_DISTANCE_MAX = 1000  # the max dist between two points on a line. If longer, then new points are added
MIN_ROAD_SEGMENT_LENGTH = 1.0         # if segment length is smaller than this, then remove point
287

288
USE_TRAM_LINES = False  # whether to build tram lines (OSM railway=tram). Often they do not merge well with roads
289

290 291 292 293
# when a static bridge model or another blocked area (e.g. on airport) intersect with a way,
# how much must at least be left so the way is kept after intersection
OVERLAP_CHECK_ROAD_MIN_REMAINING = 10

294 295 296 297 298

# =============================================================================
# PARAMETERS RELATED TO TEXTURES
# =============================================================================

299
ATLAS_SUFFIX = ''   # -- add a suffix to the atlas/atlas_LM file name
300 301 302 303
TEXTURES_ROOFS_NAME_EXCLUDE = []  # list of roof file names to exclude, e.g. ["roof_red3.png", "roof_orange.png"]
TEXTURES_FACADES_NAME_EXCLUDE = []  # e.g. ["de/commercial/facade_modern_21x42m.jpg"]
TEXTURES_ROOFS_PROVIDE_EXCLUDE = []  # list of roof provides features to exclude, e.g. ["colour:red"]
TEXTURES_FACADES_PROVIDE_EXCLUDE = []  # ditto for facade provides features, e.g. ["age:modern"]
304
TEXTURES_REGIONS_EXPLICIT = []  # list of exclusive regions to accept. All if empty
305
TEXTURES_EMPTY_LM_RGB_VALUE = 35
306 307


308 309 310 311 312 313
# =============================================================================
# PARAMETERS RELATED TO OWBB
# =============================================================================

# ==================== BUILD-ZONES GENERATION ============

314
OWBB_LANDUSE_CACHE = False
315 316
OWBB_GENERATE_LANDUSE_BUILDING_BUFFER_DISTANCE = 30
OWBB_GENERATE_LANDUSE_BUILDING_BUFFER_DISTANCE_MAX = 50
317 318
OWBB_GENERATE_LANDUSE_LANDUSE_MIN_AREA = 5000
OWBB_GENERATE_LANDUSE_LANDUSE_HOLES_MIN_AREA = 20000
319 320
OWBB_GENERATE_LANDUSE_SIMPLIFICATION_TOLERANCE = 20

321 322
OWBB_USE_BTG_LANDUSE = False  # does not work currently

323 324
OWBB_SPLIT_MADE_UP_LANDUSE_BY_MAJOR_LINES = True  # for external and generated

325 326 327 328
# the buffer around built-up land-use areas to be used for lighting of streets
# also used for buffering around water areas in cities
OWBB_BUILT_UP_BUFFER = 50
OWBB_BUILT_UP_AREA_HOLES_MIN_AREA = 100000
329
OWBB_BUILT_UP_MIN_LIT_AREA = 100000
330 331 332

OWBB_PLACE_POPULATION_DEFAULT_CITY = 200000
OWBB_PLACE_POPULATION_DEFAULT_TOWN = 20000
333
OWBB_PLACE_POPULATION_MIN_BLOCK = 15000
334 335 336 337 338 339 340
OWBB_PLACE_RADIUS_EXPONENT_CENTRE = 0.5  # 1/2
OWBB_PLACE_RADIUS_EXPONENT_BLOCK = 0.6  # 5/8
OWBB_PLACE_RADIUS_EXPONENT_DENSE = 0.666  # 2/3
OWBB_PLACE_RADIUS_FACTOR_CITY = 1.
OWBB_PLACE_RADIUS_FACTOR_TOWN = 1.

OWBB_PLACE_TILE_BORDER_EXTENSION = 10000
341

342
OWBB_PLACE_CHECK_DENSITY = False
343
OWBB_PLACE_SANITY_DENSITY = 0.15
344

345 346
# ==================== BUILDING GENERATION ============

347
OWBB_GENERATED_BUILDINGS_CACHE = False  # has only effect if OWBB_LANDUSE_CACHE = False
348
OWBB_GENERATE_BUILDINGS = False
349 350
OWBB_STEP_DISTANCE = 2  # in meters
OWBB_MIN_STREET_LENGTH = 10  # in meters
351
OWBB_MIN_CITY_BLOCK_AREA = 200  # square meters
352
OWBB_CITY_BLOCK_HIGHWAY_BUFFER = 3  # in metres buffer around highways to find city blocks
353 354 355 356 357 358

OWBB_RESIDENTIAL_HIGHWAY_MIN_GEN_SHARE = 0.3
OWBB_INDUSTRIAL_HIGHWAY_MIN_GEN_SHARE = 0.3  # FIXME: not yet used

OWBB_ZONE_AREA_MAX_GEN = 0.1  # FIXME: needs to be zone type specific and maybe village vs. town

359 360 361
OWBB_USE_GENERATED_LANDUSE_FOR_BUILDING_GENERATION = False
OWBB_USE_EXTERNAL_LANDUSE_FOR_BUILDING_GENERATION = True

362 363
OWBB_HIGHWAY_WIDTH_FACTOR = 1.3

364 365 366 367
OWBB_RESIDENTIAL_RURAL_TERRACE_SHARE = 0.1
OWBB_RESIDENTIAL_PERIPHERY_TERRACE_SHARE = 0.25
OWBB_RESIDENTIAL_RURAL_APARTMENT_SHARE = 0.1
OWBB_RESIDENTIAL_PERIPHERY_APARTMENT_SHARE = 0.3
368 369
OWBB_RESIDENTIAL_DENSE_TYPE_SHARE = {'detached': 0.1, 'terraces': 0.1, 'apartments': 0.3, 'attached': 0.5}

370 371 372
OWBB_RESIDENTIAL_TERRACE_MIN_NUMBER = 4
OWBB_RESIDENTIAL_TERRACE_TYPICAL_NUMBER = 5

373 374 375
OWBB_RESIDENTIAL_SIDE_FACTOR_PERIPHERY = 1.0
OWBB_RESIDENTIAL_SIDE_FACTOR_DENSE = 0.8
OWBB_RESIDENTIAL_BACK_FACTOR_PERIPHERY = 2.0
376 377
OWBB_RESIDENTIAL_FRONT_FACTOR_PERIPHERY = 1.0
OWBB_FRONT_DENSE = 3.0  # same for BLOCK and CENTRE
378

379
OWBB_INDUSTRIAL_LARGE_MIN_AREA = 500
380
OWBB_INDUSTRIAL_LARGE_SHARE = 0.4
381 382
OWBB_INDUSTRIAL_BUILDING_SIDE_MIN = 2.0
OWBB_INDUSTRIAL_BUILDING_SIDE_MAX = 5.0
383 384


385 386 387 388 389
# ==================== BUILDINGS LIBRARY ============
ALLOW_EMPTY_REGIONS = True
ACCEPTED_REGIONS = ['DE', 'DK']


390 391
# default_args_end # DO NOT MODIFY THIS LINE

392 393 394 395 396 397
def get_output_path():
    if PATH_TO_OUTPUT:
        return PATH_TO_OUTPUT
    return PATH_TO_SCENERY


398
def get_center_global():
399 400
    cmin = v.Vec2d(BOUNDARY_WEST, BOUNDARY_SOUTH)
    cmax = v.Vec2d(BOUNDARY_EAST, BOUNDARY_NORTH)
401 402
    return (cmin + cmax) * 0.5

403

404
def get_extent_global():
405 406
    cmin = v.Vec2d(BOUNDARY_WEST, BOUNDARY_SOUTH)
    cmax = v.Vec2d(BOUNDARY_EAST, BOUNDARY_NORTH)
407
    return cmin, cmax
408

409

410 411 412 413 414
def get_tile_index() -> int:
    lon_lat = get_center_global()
    return ct.calc_tile_index((lon_lat.lon, lon_lat.lat))


415 416 417 418 419
def get_clipping_border():
    rect = [(BOUNDARY_WEST, BOUNDARY_SOUTH),
            (BOUNDARY_EAST, BOUNDARY_SOUTH),
            (BOUNDARY_EAST, BOUNDARY_NORTH),
            (BOUNDARY_WEST, BOUNDARY_NORTH)]
420 421
    return rect

422

423
def _check_ratio_dict_parameter(ratio_dict: typing.Optional[typing.Dict], name: str, is_int: bool=True) -> None:
424 425 426 427 428 429 430
    if ratio_dict is None:
        raise ValueError('Parameter {} must not be None'.format(name))
    if not isinstance(ratio_dict, dict):
        raise ValueError('Parameter {} must be a dict'.format(name))
    if len(ratio_dict) == 0:
        raise ValueError('Parameter %s must not be an empty dict'.format(name))
    total = 0.
431
    prev_key = -9999
432
    for key, ratio in ratio_dict.items():
433 434 435 436 437 438 439 440 441
        if is_int:
            if not isinstance(key, int):
                raise ValueError('key {} in parameter {} must be an int'.format(str(key), name))
            if prev_key > key:
                raise ValueError('key {} in parameter {} must be larger than previous key'.format(str(key), name))
            prev_key = key
        else:
            if not isinstance(key, str):
                raise ValueError('key {} in parameter {} must be a string'.format(str(key), name))
442 443 444 445 446 447 448
        if not isinstance(ratio, float):
            raise ValueError('ratio {} for key {} in param {} must be a float'.format(str(ratio), str(key), name))
        total += ratio
    if abs(total - 1) > 0.001:
        raise ValueError('The total of all ratios in param {} must be 1'.format(name))


449
def show():
450
    """
451
    Prints all parameters as key = value if log level is INFO or lower
452
    """
453
    if ulog.log_level_info_or_lower():
454
        print('--- Using the following parameters: ---')
455
        my_globals = globals()
456
        for k in sorted(my_globals.keys()):
457 458 459 460 461 462
            if k.startswith('__'):
                continue
            elif k == "args":
                continue
            elif k == "parser":
                continue
463
            elif isinstance(my_globals[k], type) or \
464 465 466 467
                    isinstance(my_globals[k], types.FunctionType) or \
                    isinstance(my_globals[k], types.ModuleType):
                continue
            else:
468 469
                print('%s = %s' % (k, my_globals[k]))
        print('------')
470

471

472
def read_from_file(filename):
473 474
    logging.info('Reading parameters from file: %s' % filename)
    default_globals = globals()
475
    file_globals = dict()
476
    try:
477 478
        exec(compile(open(filename).read(), filename, 'exec'), file_globals)
    except IOError as reason:
479
        logging.error("Error processing file with parameters: %s", reason)
480 481
        sys.exit(1)
    except NameError:
482
        logging.error(traceback.format_exc())
483
        logging.error("Error while reading " + filename + ". Perhaps an unquoted string in your parameters file?")
484
        sys.exit(1)
485

486
    has_unknown_parameters = False
487
    for k, v in file_globals.items():
488
        if k.startswith('_'):
489 490 491 492 493
            continue
        k = k.upper()
        if k in default_globals:
            default_globals[k] = v
        else:
494 495 496 497
            logging.error('Unknown parameter: %s=%s' % (k, v))
            has_unknown_parameters = True
    if has_unknown_parameters:
        sys.exit(1)
498

499 500 501 502 503 504 505 506 507
    # correct use of parameter PATH_TO_SCENERY_OPT: earlier only string, now list of strings (or None)
    global PATH_TO_SCENERY_OPT
    if PATH_TO_SCENERY_OPT:
        if isinstance(PATH_TO_SCENERY_OPT, str):
            if PATH_TO_SCENERY_OPT == "":
                PATH_TO_SCENERY_OPT = None
            else:
                PATH_TO_SCENERY_OPT = [PATH_TO_SCENERY_OPT]

508 509 510 511 512 513
    # check the ratios in specific parameters
    global BUILDING_NUMBER_LEVELS_CENTRE
    global BUILDING_NUMBER_LEVELS_BLOCK
    global BUILDING_NUMBER_LEVELS_DENSE
    global BUILDING_NUMBER_LEVELS_PERIPHERY
    global BUILDING_NUMBER_LEVELS_RURAL
514 515 516 517
    global BUILDING_NUMBER_LEVELS_APARTMENTS
    global BUILDING_NUMBER_LEVELS_INDUSTRIAL
    global BUILDING_NUMBER_LEVELS_OTHER

518
    global BUILDING_ROOF_SHAPE_RATIO
519
    global OWBB_RESIDENTIAL_DENSE_TYPE_SHARE
520

521 522 523 524 525
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_CENTRE, 'BUILDING_NUMBER_LEVELS_CENTRE')
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_BLOCK, 'BUILDING_NUMBER_LEVELS_BLOCK')
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_DENSE, 'BUILDING_NUMBER_LEVELS_DENSE')
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_PERIPHERY, 'BUILDING_NUMBER_LEVELS_PERIPHERY')
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_RURAL, 'BUILDING_NUMBER_LEVELS_RURAL')
526 527 528
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_APARTMENTS, 'BUILDING_NUMBER_LEVELS_APARTMENTS')
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_INDUSTRIAL, 'BUILDING_NUMBER_LEVELS_INDUSTRIAL')
    _check_ratio_dict_parameter(BUILDING_NUMBER_LEVELS_OTHER, 'BUILDING_NUMBER_LEVELS_OTHER')
529
    _check_ratio_dict_parameter(BUILDING_ROOF_SHAPE_RATIO, 'BUILDING_ROOF_SHAPE_RATIO', False)
530
    _check_ratio_dict_parameter(OWBB_RESIDENTIAL_DENSE_TYPE_SHARE, 'OWBB_RESIDENTIAL_DENSE_TYPE_SHARE', False)
531

532

533 534 535 536 537 538 539 540 541 542 543 544 545
def show_default():
    """show default parameters by printing all params defined above between
        # default_args_start and # default_args_end to screen.
    """
    f = open(sys.argv[0], 'r')
    do_print = False
    for line in f.readlines():
        if line.startswith('# default_args_start'):
            do_print = True
            continue
        elif line.startswith('# default_args_end'):
            return
        if do_print:
546
            print(line, end='')
547 548


549 550 551 552 553
def set_boundary(boundary_west: float, boundary_south: float,
                 boundary_east: float, boundary_north: float) -> None:
    """Overrides the geographical boundary values (either default values or read from file).
    In most situations should be called after method read_from_file().
    """
554
    import utils.utilities as uu
555
    try:
556 557
        uu.check_boundary(boundary_west, boundary_south, boundary_east, boundary_north)
    except uu.BoundaryError as be:
558
        logging.error(be.message)
559
        sys.exit(1)
560

561 562 563 564 565 566 567 568 569 570
    global BOUNDARY_WEST
    BOUNDARY_WEST = boundary_west
    global BOUNDARY_SOUTH
    BOUNDARY_SOUTH = boundary_south
    global BOUNDARY_EAST
    BOUNDARY_EAST = boundary_east
    global BOUNDARY_NORTH
    BOUNDARY_NORTH = boundary_north


571 572 573 574 575 576
if __name__ == "__main__":
    # Handling arguments and parameters
    parser = argparse.ArgumentParser(
        description="The parameters module provides parameters to osm2city - used as main it shows the parameters used.")
    parser.add_argument("-f", "--file", dest="filename",
                        help="read parameters from FILE (e.g. params.ini)", metavar="FILE")
577
    parser.add_argument("-d", "--show-default", action="store_true", help="show default parameters")
578 579 580
    args = parser.parse_args()
    if args.filename is not None:
        read_from_file(args.filename)
581
        show()
582 583
    if args.show_default:
        show_default()
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606


# ================ UNITTESTS =======================


class TestParameters(unittest.TestCase):
    def test_check_ratio_dict_parameter(self):
        my_ratio_dict = None
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = list()
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = dict()
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = {'A': 'B'}
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = {1: 'b'}
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = {1: 0.01, 2: 1.}
607 608 609
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = {2: 0.01, 1: .99}
610 611 612 613
        with self.assertRaises(ValueError):
            _check_ratio_dict_parameter(my_ratio_dict, 'my_ratio_dict')
        my_ratio_dict = {1: 0.01, 2: 0.99}
        self.assertEqual(2, len(my_ratio_dict), 'Length correct and no exception')