mirror of
https://github.com/enzet/map-machine.git
synced 2025-06-01 10:21:54 +02:00
Fix inner path and outer path drawing. Make flinger more simple.
This commit is contained in:
parent
8d8080181e
commit
f6a15e7a71
10 changed files with 478 additions and 410 deletions
333
data/tags.yml
333
data/tags.yml
|
@ -3,55 +3,65 @@ colors:
|
||||||
# Entity
|
# Entity
|
||||||
|
|
||||||
background: "EEEEEE"
|
background: "EEEEEE"
|
||||||
grass: "C8DC94"
|
|
||||||
sand: "F0E0D0"
|
|
||||||
beach: "F0E0C0"
|
beach: "F0E0C0"
|
||||||
deciduous: "FCAF3E"
|
deciduous: "FCAF3E"
|
||||||
desert: "F0E0D0"
|
desert: "F0E0D0"
|
||||||
evergreen: "688C44"
|
evergreen: "688C44"
|
||||||
playground: "884400"
|
grass: "C8DC94"
|
||||||
parking: "DDCC99"
|
parking: "DDCC99"
|
||||||
|
playground: "884400"
|
||||||
|
sand: "F0E0D0"
|
||||||
|
tree: "98AC64"
|
||||||
water: "AACCFF"
|
water: "AACCFF"
|
||||||
water_border: "6688BB"
|
water_border: "6688BB"
|
||||||
wood: "B8CC84"
|
wood: "B8CC84"
|
||||||
tree: "98AC64"
|
|
||||||
|
|
||||||
direction_view_color: "E0F0FF"
|
direction_view_color: "E0F0FF"
|
||||||
direction_camera_color: "0088FF"
|
direction_camera_color: "0088FF"
|
||||||
|
|
||||||
outline_color: "FFFFFF"
|
|
||||||
beach_color: "F0E0C0"
|
beach_color: "F0E0C0"
|
||||||
boundary_color: "880088"
|
boundary_color: "880088"
|
||||||
|
building_border_color: "E0D0C0" # "AAAAAA"
|
||||||
building_color: "F8F0E8" # "D0D0C0"
|
building_color: "F8F0E8" # "D0D0C0"
|
||||||
building_border_color: "DDDDDD" # "AAAAAA"
|
|
||||||
construction_color: "CCCCCC"
|
construction_color: "CCCCCC"
|
||||||
cycle_color: "4444EE"
|
cycle_color: "4444EE"
|
||||||
desert_color: "F0E0D0"
|
desert_color: "F0E0D0"
|
||||||
foot_color: "B89A74"
|
emergency_color: "DD2222"
|
||||||
indoor_color: "E8E4E0"
|
farmland_color: "FFEEBB"
|
||||||
indoor_border_color: "C0B8B0"
|
|
||||||
foot_border_color: "FFFFFF"
|
foot_border_color: "FFFFFF"
|
||||||
grass_color: "CFE0A8"
|
foot_color: "B89A74"
|
||||||
grass_border_color: "BFD098"
|
grass_border_color: "BFD098"
|
||||||
|
grass_color: "CFE0A8"
|
||||||
guide_strips_color: "228833"
|
guide_strips_color: "228833"
|
||||||
|
indoor_border_color: "C0B8B0"
|
||||||
|
indoor_color: "E8E4E0"
|
||||||
|
meadow_border_color: "BFD078"
|
||||||
|
meadow_color: "CFE088"
|
||||||
|
orchard_color: "B8DCA4"
|
||||||
|
outline_color: "FFFFFF"
|
||||||
parking_color: "DDCC99"
|
parking_color: "DDCC99"
|
||||||
platform_color: "CCCCCC"
|
|
||||||
platform_border_color: "AAAAAA"
|
platform_border_color: "AAAAAA"
|
||||||
|
platform_color: "CCCCCC"
|
||||||
|
playground_border_color: "663300"
|
||||||
playground_color: "884400"
|
playground_color: "884400"
|
||||||
primary_border_color: "888888" # "AA8800"
|
primary_border_color: "AA8800"
|
||||||
primary_color: "FFFFFF" # "FFDD66"
|
primary_color: "FFDD66"
|
||||||
private_access_color: "884444"
|
private_access_color: "884444"
|
||||||
ridge_color: "000000"
|
ridge_color: "000000"
|
||||||
road_border_color: "CCCCCC"
|
road_border_color: "CCCCCC"
|
||||||
rock_color: "DDDDDD"
|
rock_color: "DDDDDD"
|
||||||
route_color: "FFFFFF"
|
route_color: "FFFFFF"
|
||||||
sand_color: "F0E0D0"
|
sand_color: "F0E0D0"
|
||||||
|
secondary_border_color: "BB9911"
|
||||||
|
secondary_color: "FFEE77"
|
||||||
scree_color: "CCCCCC"
|
scree_color: "CCCCCC"
|
||||||
water_color: "AACCFF"
|
tertiary_border_color: "CCAA22"
|
||||||
|
tertiary_color: "FFFF88"
|
||||||
water_border_color: "6688BB"
|
water_border_color: "6688BB"
|
||||||
|
water_color: "AACCFF"
|
||||||
wetland_color: "BFE0D8"
|
wetland_color: "BFE0D8"
|
||||||
wood_color: "B8CC84"
|
|
||||||
wood_border_color: "A8BC74"
|
wood_border_color: "A8BC74"
|
||||||
|
wood_color: "B8CC84"
|
||||||
|
|
||||||
# Colors not in W3C
|
# Colors not in W3C
|
||||||
|
|
||||||
|
@ -112,12 +122,17 @@ tags:
|
||||||
|
|
||||||
# Emergency
|
# Emergency
|
||||||
|
|
||||||
- tags: {emergency: phone}
|
- tags: {emergency: defibrillator}
|
||||||
icon: [sos_phone]
|
icon: [defibrillator]
|
||||||
|
color: emergency_color
|
||||||
- tags: {emergency: fire_extinguisher}
|
- tags: {emergency: fire_extinguisher}
|
||||||
icon: [fire_extinguisher]
|
icon: [fire_extinguisher]
|
||||||
|
color: emergency_color
|
||||||
- tags: {emergency: fire_hydrant}
|
- tags: {emergency: fire_hydrant}
|
||||||
icon: [fire_hydrant]
|
icon: [fire_hydrant]
|
||||||
|
- tags: {emergency: phone}
|
||||||
|
icon: [sos_phone]
|
||||||
|
color: emergency_color
|
||||||
|
|
||||||
# Highway
|
# Highway
|
||||||
|
|
||||||
|
@ -152,9 +167,13 @@ tags:
|
||||||
icon: [pole]
|
icon: [pole]
|
||||||
- tags: {power: generator}
|
- tags: {power: generator}
|
||||||
icon: [power_generator]
|
icon: [power_generator]
|
||||||
|
- tags: {power: generator, generator:source: solar}
|
||||||
|
icon: [solar_panel]
|
||||||
- tags: {power: tower}
|
- tags: {power: tower}
|
||||||
icon: [power_tower]
|
icon: [power_tower]
|
||||||
|
|
||||||
|
- tags: {tourism: "*"}
|
||||||
|
icon: [historic]
|
||||||
- tags: {information: "*"}
|
- tags: {information: "*"}
|
||||||
icon: [information]
|
icon: [information]
|
||||||
- tags: {tourism: information}
|
- tags: {tourism: information}
|
||||||
|
@ -602,6 +621,8 @@ tags:
|
||||||
|
|
||||||
# Tourism
|
# Tourism
|
||||||
|
|
||||||
|
- tags: {tourism: artwork}
|
||||||
|
icon: [picture]
|
||||||
- tags: {tourism: artwork, artwork_type: statue}
|
- tags: {tourism: artwork, artwork_type: statue}
|
||||||
icon: [statue]
|
icon: [statue]
|
||||||
- tags: {tourism: artwork, artwork_type: sculpture}
|
- tags: {tourism: artwork, artwork_type: sculpture}
|
||||||
|
@ -665,87 +686,111 @@ ways:
|
||||||
stroke: indoor_border_color
|
stroke: indoor_border_color
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
fill: indoor_color
|
fill: indoor_color
|
||||||
layer: 10
|
priority: 10
|
||||||
- tags: {indoor: corridor}
|
- tags: {indoor: corridor}
|
||||||
stroke: indoor_color
|
stroke: indoor_color
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
fill: indoor_color
|
fill: indoor_color
|
||||||
layer: 11
|
priority: 11
|
||||||
- tags: {indoor: ["yes", room, elevator]}
|
- tags: {highway: corridor}
|
||||||
|
stroke: "#00FF00"
|
||||||
|
stroke-width: 5
|
||||||
|
priority: 11
|
||||||
|
- tags: {indoor: ["yes", room, elevator], area: "yes"}
|
||||||
stroke: indoor_color
|
stroke: indoor_color
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
fill: indoor_color
|
fill: indoor_color
|
||||||
layer: 12
|
priority: 12
|
||||||
- tags: {indoor: column}
|
- tags: {indoor: column}
|
||||||
stroke: indoor_color
|
stroke: indoor_color
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
fill: indoor_color
|
fill: indoor_color
|
||||||
layer: 13
|
priority: 13
|
||||||
|
|
||||||
|
- tags: {power: line}
|
||||||
|
stroke: "#000000"
|
||||||
|
stroke-width: 1
|
||||||
|
opacity: 0.2
|
||||||
|
priority: 80
|
||||||
|
|
||||||
- tags: {natural: wood}
|
- tags: {natural: wood}
|
||||||
fill: wood_color
|
fill: wood_color
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: wetland}
|
- tags: {natural: wetland}
|
||||||
fill: wetland_color
|
fill: wetland_color
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: grassland}
|
- tags: {natural: grassland}
|
||||||
fill: grass_color
|
fill: grass_color
|
||||||
stroke: grass_border_color
|
stroke: grass_border_color
|
||||||
layer: 20
|
priority: 20
|
||||||
- tags: {natural: scrub}
|
- tags: {natural: scrub}
|
||||||
fill: wood_color
|
fill: wood_color
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: sand}
|
- tags: {natural: sand}
|
||||||
fill: sand_color
|
fill: sand_color
|
||||||
layer: 20
|
priority: 20
|
||||||
- tags: {natural: beach}
|
- tags: {natural: beach}
|
||||||
fill: beach_color
|
fill: beach_color
|
||||||
layer: 20
|
priority: 20
|
||||||
- tags: {natural: desert}
|
- tags: {natural: desert}
|
||||||
fill: desert_color
|
fill: desert_color
|
||||||
layer: 20
|
priority: 20
|
||||||
- tags: {natural: forest}
|
- tags: {natural: forest}
|
||||||
fill: wood_color
|
fill: wood_color
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: tree_row}
|
- tags: {natural: tree_row}
|
||||||
layer: 21
|
priority: 21
|
||||||
stroke: wood_color
|
stroke: wood_color
|
||||||
stroke-width: 5
|
stroke-width: 5
|
||||||
- tags: {natural: water}
|
- tags: {natural: water}
|
||||||
# fill: water_color
|
fill: water_color
|
||||||
stroke: water_border_color
|
# stroke: water_border_color
|
||||||
stroke-width: 1
|
# stroke-width: 1
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: coastline}
|
- tags: {natural: coastline}
|
||||||
# fill: water_color
|
# fill: water_color
|
||||||
stroke: water_border_color
|
stroke: water_border_color
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: ridge}
|
- tags: {natural: ridge}
|
||||||
stroke-width: 2
|
stroke-width: 2
|
||||||
opacity: 0.3
|
opacity: 0.3
|
||||||
stroke: ridge_color
|
stroke: ridge_color
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {natural: bare_rock}
|
- tags: {natural: bare_rock}
|
||||||
fill: rock_color
|
fill: rock_color
|
||||||
- tags: {natural: scree}
|
- tags: {natural: scree}
|
||||||
fill: scree_color
|
fill: scree_color
|
||||||
|
|
||||||
- tags: {landuse: grass}
|
|
||||||
fill: grass_color
|
|
||||||
layer: 20
|
|
||||||
stroke: grass_border_color
|
|
||||||
- tags: {landuse: conservation}
|
- tags: {landuse: conservation}
|
||||||
fill: grass_color
|
fill: grass_color
|
||||||
layer: 20
|
priority: 20
|
||||||
- tags: {landuse: forest}
|
|
||||||
fill: wood_color
|
|
||||||
layer: 20
|
|
||||||
- tags: {landuse: garages}
|
|
||||||
fill: parking_color
|
|
||||||
layer: 21
|
|
||||||
- tags: {landuse: construction}
|
- tags: {landuse: construction}
|
||||||
fill: construction_color
|
fill: construction_color
|
||||||
|
- tags: {landuse: farmland}
|
||||||
|
fill: farmland_color
|
||||||
|
priority: 20
|
||||||
|
stroke: grass_border_color
|
||||||
|
- tags: {landuse: forest}
|
||||||
|
fill: wood_color
|
||||||
|
priority: 20
|
||||||
|
- tags: {landuse: garages}
|
||||||
|
fill: parking_color
|
||||||
|
priority: 21
|
||||||
|
- tags: {landuse: grass}
|
||||||
|
fill: grass_color
|
||||||
|
priority: 20
|
||||||
|
stroke: grass_border_color
|
||||||
|
- tags: {landuse: orchard}
|
||||||
|
fill: orchard_color
|
||||||
|
priority: 21
|
||||||
|
- tags: {landuse: meadow}
|
||||||
|
fill: meadow_color
|
||||||
|
priority: 20
|
||||||
|
stroke: meadow_border_color
|
||||||
|
|
||||||
|
# Hidden land use
|
||||||
|
|
||||||
- tags: {landuse: residential}
|
- tags: {landuse: residential}
|
||||||
fill: none
|
fill: none
|
||||||
stroke: none
|
stroke: none
|
||||||
|
@ -755,10 +800,16 @@ ways:
|
||||||
- tags: {landuse: military}
|
- tags: {landuse: military}
|
||||||
fill: none
|
fill: none
|
||||||
stroke: none
|
stroke: none
|
||||||
|
- tags: {landuse: railway}
|
||||||
|
fill: none
|
||||||
|
stroke: none
|
||||||
|
|
||||||
- tags: {building: "*"}
|
- tags: {building: "*"}
|
||||||
fill: building_color
|
fill: building_color
|
||||||
stroke: building_border_color
|
stroke: building_border_color
|
||||||
|
#- tags: {building:part: "*"}
|
||||||
|
# fill: building_color
|
||||||
|
# stroke: building_border_color
|
||||||
|
|
||||||
- tags: {amenity: parking}
|
- tags: {amenity: parking}
|
||||||
fill: parking_color
|
fill: parking_color
|
||||||
|
@ -782,215 +833,257 @@ ways:
|
||||||
- tags: {railway: subway}
|
- tags: {railway: subway}
|
||||||
stroke-width: 10
|
stroke-width: 10
|
||||||
stroke: "#DDDDDD"
|
stroke: "#DDDDDD"
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {railway: [narrow_gauge, tram]}
|
- tags: {railway: [rail, narrow_gauge, tram]}
|
||||||
stroke-width: 2
|
stroke-width: 2
|
||||||
stroke: "#000000"
|
stroke: "#000000"
|
||||||
layer: 41
|
priority: 43
|
||||||
- tags: {railway: platform}
|
- tags: {railway: platform}
|
||||||
fill: platform_color
|
fill: platform_color
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
stroke: platform_border_color
|
stroke: platform_border_color
|
||||||
layer: 41
|
priority: 41
|
||||||
|
|
||||||
- tags: {highway: motorway}
|
- tags: {highway: motorway}
|
||||||
stroke-width: 33
|
r2: 15
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: trunk}
|
- tags: {highway: trunk}
|
||||||
stroke-width: 31
|
r2: 13
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: primary}
|
- tags: {highway: primary}
|
||||||
stroke-width: 29
|
r2: 11
|
||||||
stroke: primary_border_color
|
stroke: primary_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41.9
|
||||||
|
- tags: {highway: motorway_link}
|
||||||
|
r2: 9
|
||||||
|
stroke: road_border_color
|
||||||
|
stroke-linecap: round
|
||||||
|
priority: 41
|
||||||
- tags: {highway: secondary}
|
- tags: {highway: secondary}
|
||||||
stroke-width: 27
|
r2: 9
|
||||||
stroke: road_border_color
|
stroke: secondary_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41.8
|
||||||
- tags: {highway: tertiary}
|
- tags: {highway: tertiary}
|
||||||
stroke-width: 25
|
r2: 7
|
||||||
stroke: road_border_color
|
stroke: tertiary_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41.7
|
||||||
- tags: {highway: unclassified}
|
- tags: {highway: unclassified}
|
||||||
stroke-width: 17
|
r2: 5
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: residential}
|
- tags: {highway: residential}
|
||||||
stroke-width: 17
|
r2: 5
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
|
- tags: {highway: living_street}
|
||||||
|
r2: 4
|
||||||
|
stroke: road_border_color
|
||||||
|
stroke-linecap: round
|
||||||
|
priority: 41
|
||||||
- tags: {highway: service}
|
- tags: {highway: service}
|
||||||
no_tags: {service: parking_aisle}
|
no_tags: {service: parking_aisle}
|
||||||
stroke-width: 11
|
r2: 3
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: service, service: parking_aisle}
|
- tags: {highway: service, service: parking_aisle}
|
||||||
stroke-width: 7
|
r2: 2
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: track}
|
- tags: {highway: track}
|
||||||
stroke-width: 3
|
stroke-width: 3
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: [footway, pedestrian, cycleway]}
|
- tags: {highway: [footway, pedestrian, cycleway]}
|
||||||
no_tags: {area: "yes"}
|
no_tags: {area: "yes"}
|
||||||
stroke-width: 3
|
stroke-width: 3
|
||||||
stroke: foot_border_color
|
stroke: foot_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 41
|
priority: 41
|
||||||
- tags: {highway: steps}
|
- tags: {highway: steps}
|
||||||
stroke-width: 6
|
stroke-width: 6
|
||||||
stroke: foot_border_color
|
stroke: foot_border_color
|
||||||
stroke-linecap: butt
|
stroke-linecap: butt
|
||||||
|
- tags: {highway: path}
|
||||||
|
stroke-width: 3
|
||||||
|
stroke: foot_border_color
|
||||||
|
priority: 41
|
||||||
|
|
||||||
- tags: {highway: motorway}
|
- tags: {highway: motorway}
|
||||||
stroke-width: 31
|
r: 15
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: trunk}
|
- tags: {highway: trunk}
|
||||||
stroke-width: 29
|
r: 13
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: primary}
|
- tags: {highway: primary}
|
||||||
stroke-width: 27
|
r: 11
|
||||||
stroke: primary_color
|
stroke: primary_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42.9
|
||||||
- tags: {highway: secondary}
|
- tags: {highway: secondary}
|
||||||
stroke-width: 25
|
r: 9
|
||||||
|
stroke: secondary_color
|
||||||
|
stroke-linecap: round
|
||||||
|
priority: 42.8
|
||||||
|
- tags: {highway: motorway_link}
|
||||||
|
r: 9
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: tertiary}
|
- tags: {highway: tertiary}
|
||||||
stroke-width: 23
|
r: 7
|
||||||
stroke: "#FFFFFF"
|
stroke: tertiary_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42.7
|
||||||
- tags: {highway: unclassified}
|
- tags: {highway: unclassified}
|
||||||
stroke-width: 15
|
r: 5
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: residential}
|
- tags: {highway: residential}
|
||||||
stroke-width: 15
|
r: 5
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: service, service: parking_aisle}
|
- tags: {highway: living_street}
|
||||||
stroke-width: 5
|
r: 4
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: service}
|
- tags: {highway: service}
|
||||||
no_tags: {service: parking_aisle}
|
no_tags: {service: parking_aisle}
|
||||||
stroke-width: 9
|
r: 3
|
||||||
stroke: "#FFFFFF"
|
stroke: "#FFFFFF"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
|
- tags: {highway: service, service: parking_aisle}
|
||||||
|
r: 2
|
||||||
|
stroke: "#FFFFFF"
|
||||||
|
stroke-linecap: round
|
||||||
|
priority: 42
|
||||||
- tags: {highway: track}
|
- tags: {highway: track}
|
||||||
stroke-width: 3
|
stroke-width: 3
|
||||||
stroke: road_border_color
|
stroke: road_border_color
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: [footway, pedestrian]}
|
- tags: {highway: [footway, pedestrian]}
|
||||||
no_tags: {area: "yes"}
|
no_tags: {area: "yes"}
|
||||||
stroke-width: 1.5
|
stroke-width: 1.5
|
||||||
stroke-dasharray: 7,3
|
stroke-dasharray: 7,3
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
stroke: foot_color
|
stroke: foot_color
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: [footway, pedestrian], area: "yes"}
|
- tags: {highway: [footway, pedestrian], area: "yes"}
|
||||||
stroke: none
|
stroke: none
|
||||||
fill: "#DDDDDD"
|
fill: "#DDDDDD"
|
||||||
stroke-linecap: round
|
stroke-linecap: round
|
||||||
layer: -55 # FIXME
|
priority: -55 # FIXME
|
||||||
- tags: {highway: cycleway}
|
- tags: {highway: cycleway}
|
||||||
no_tags: {area: "yes"}
|
no_tags: {area: "yes"}
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
stroke: cycle_color
|
stroke: cycle_color
|
||||||
stroke-dasharray: 8,2
|
stroke-dasharray: 8,2
|
||||||
stroke-linecap: butt
|
stroke-linecap: butt
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: steps, conveying: "*"}
|
- tags: {highway: steps, conveying: "*"}
|
||||||
stroke-width: 5
|
stroke-width: 5
|
||||||
stroke-dasharray: 1.5,2
|
stroke-dasharray: 1.5,2
|
||||||
stroke-linecap: butt
|
stroke-linecap: butt
|
||||||
stroke: "#888888"
|
stroke: "#888888"
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: steps}
|
- tags: {highway: steps}
|
||||||
no_tags: {conveying: "*"}
|
no_tags: {conveying: "*"}
|
||||||
stroke-width: 5
|
stroke-width: 5
|
||||||
stroke-dasharray: 1.5,2
|
stroke-dasharray: 1.5,2
|
||||||
stroke-linecap: butt
|
stroke-linecap: butt
|
||||||
stroke: foot_color
|
stroke: foot_color
|
||||||
layer: 42
|
priority: 42
|
||||||
- tags: {highway: path}
|
- tags: {highway: path}
|
||||||
stroke-width: 1
|
stroke-width: 1.5
|
||||||
stroke-dasharray: 5,5
|
stroke-dasharray: 5,3
|
||||||
stroke-linecap: butt
|
stroke-linecap: butt
|
||||||
stroke: foot_color
|
stroke: foot_color
|
||||||
layer: 42
|
priority: 42
|
||||||
|
|
||||||
- tags: {route: ferry}
|
- tags: {route: ferry}
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
stroke-dasharray: 3,3
|
stroke-dasharray: 3,3
|
||||||
stroke-linecap: butt
|
stroke-linecap: butt
|
||||||
stroke: route_color
|
stroke: route_color
|
||||||
layer: 42
|
priority: 42
|
||||||
|
|
||||||
|
- tags: {leisure: garden}
|
||||||
|
fill: grass_color
|
||||||
|
priority: 21
|
||||||
|
- tags: {leisure: park}
|
||||||
|
fill: grass_color
|
||||||
|
opacity: 0.5
|
||||||
|
- tags: {leisure: pitch}
|
||||||
|
fill: playground_color
|
||||||
|
stroke: playground_border_color
|
||||||
|
stroke-width: 1
|
||||||
|
opacity: 0.2
|
||||||
|
priority: 21
|
||||||
- tags: {leisure: playground}
|
- tags: {leisure: playground}
|
||||||
fill: playground_color
|
fill: playground_color
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
icon: toy_horse
|
icon: toy_horse
|
||||||
layer: 21
|
priority: 21
|
||||||
- tags: {leisure: garden}
|
- tags: {leisure: swimming_pool}
|
||||||
fill: grass_color
|
fill: water_color
|
||||||
layer: 21
|
stroke: water_border_color
|
||||||
- tags: {leisure: pitch}
|
stroke-width: 1
|
||||||
fill: playground_color
|
|
||||||
opacity: 0.2
|
|
||||||
layer: 21
|
|
||||||
- tags: {leisure: park}
|
|
||||||
fill: grass_color
|
|
||||||
opacity: 0.5
|
|
||||||
|
|
||||||
- tags: {barrier: hedge}
|
- tags: {barrier: hedge}
|
||||||
fill: none
|
fill: none
|
||||||
stroke: wood_color
|
stroke: wood_color
|
||||||
stroke-width: 4
|
stroke-width: 4
|
||||||
layer: 40
|
priority: 40
|
||||||
|
- tags: {barrier: city_wall}
|
||||||
|
fill: none
|
||||||
|
stroke: "#000000"
|
||||||
|
stroke-width: 1
|
||||||
|
opacity: 0.6
|
||||||
|
priority: 40
|
||||||
|
- tags: {barrier: wall}
|
||||||
|
fill: none
|
||||||
|
stroke: "#000000"
|
||||||
|
stroke-width: 1
|
||||||
|
opacity: 0.5
|
||||||
|
priority: 40
|
||||||
- tags: {barrier: [fence, retaining_wall]}
|
- tags: {barrier: [fence, retaining_wall]}
|
||||||
fill: none
|
fill: none
|
||||||
stroke: "#000000"
|
stroke: "#000000"
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
opacity: 0.4
|
opacity: 0.4
|
||||||
layer: 40
|
priority: 40
|
||||||
- tags: {barrier: handrail}
|
- tags: {barrier: handrail}
|
||||||
fill: none
|
fill: none
|
||||||
stroke: "#000000"
|
stroke: "#000000"
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
opacity: 0.3
|
opacity: 0.3
|
||||||
layer: 40
|
priority: 40
|
||||||
- tags: {barrier: kerb}
|
- tags: {barrier: kerb}
|
||||||
fill: none
|
fill: none
|
||||||
stroke: "#000000"
|
stroke: "#000000"
|
||||||
stroke-width: 1
|
stroke-width: 1
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
layer: 40
|
priority: 40
|
||||||
|
|
||||||
- tags: {border: "*"}
|
- tags: {border: "*"}
|
||||||
stroke: "#FF0000"
|
stroke: "#FF0000"
|
||||||
|
@ -1002,7 +1095,7 @@ ways:
|
||||||
stroke: boundary_color
|
stroke: boundary_color
|
||||||
stroke-width: 0.3
|
stroke-width: 0.3
|
||||||
stroke-dasharray: 10,5
|
stroke-dasharray: 10,5
|
||||||
layer: 60
|
priority: 60
|
||||||
|
|
||||||
tags_to_write: [
|
tags_to_write: [
|
||||||
"operator", "opening_hours", "cuisine", "network", "website",
|
"operator", "opening_hours", "cuisine", "network", "website",
|
||||||
|
|
|
@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
||||||
from roentgen.flinger import GeoFlinger
|
from roentgen.flinger import Flinger
|
||||||
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay, OSMNode
|
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay, OSMNode
|
||||||
from roentgen.scheme import IconSet, Scheme
|
from roentgen.scheme import IconSet, Scheme
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
@ -19,20 +19,47 @@ from roentgen.util import MinMax
|
||||||
DEBUG: bool = False
|
DEBUG: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
def is_clockwise(polygon: List[OSMNode]) -> bool:
|
||||||
|
"""
|
||||||
|
Are polygon nodes are in clockwise order.
|
||||||
|
"""
|
||||||
|
count: float = 0
|
||||||
|
for index in range(len(polygon)): # type: int
|
||||||
|
next_index: int = 0 if index == len(polygon) - 1 else index + 1
|
||||||
|
count += (
|
||||||
|
(polygon[next_index].position[0] - polygon[index].position[0]) *
|
||||||
|
(polygon[next_index].position[1] + polygon[index].position[1]))
|
||||||
|
return count >= 0
|
||||||
|
|
||||||
|
|
||||||
|
def make_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
|
||||||
|
if is_clockwise(polygon):
|
||||||
|
return polygon
|
||||||
|
else:
|
||||||
|
return list(reversed(polygon))
|
||||||
|
|
||||||
|
|
||||||
|
def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
|
||||||
|
if not is_clockwise(polygon):
|
||||||
|
return polygon
|
||||||
|
else:
|
||||||
|
return list(reversed(polygon))
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
"""
|
"""
|
||||||
Node in Röntgen terms.
|
Node in Röntgen terms.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, icon_set: IconSet, tags: Dict[str, str],
|
self, icon_set: IconSet, tags: Dict[str, str],
|
||||||
point: (float, float), path: Optional[str],
|
point: np.array, coordinates: np.array,
|
||||||
priority: int = 0, is_for_node: bool = True):
|
priority: int = 0, is_for_node: bool = True):
|
||||||
assert point is not None
|
assert point is not None
|
||||||
|
|
||||||
self.icon_set: IconSet = icon_set
|
self.icon_set: IconSet = icon_set
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.point = point
|
self.point: np.array = point
|
||||||
self.path = path
|
self.coordinates: np.array = coordinates
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.layer = 0
|
self.layer = 0
|
||||||
self.is_for_node = is_for_node
|
self.is_for_node = is_for_node
|
||||||
|
@ -48,43 +75,50 @@ class Way:
|
||||||
Way in Röntgen terms.
|
Way in Röntgen terms.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, kind: str, nodes: List[OSMNode], path, style: Dict[str, Any],
|
self, kind: str, inners, outers, style: Dict[str, Any],
|
||||||
layer: float = 0.0, priority: float = 0, levels=None):
|
layer: float = 0.0, levels=None):
|
||||||
assert nodes or path
|
|
||||||
|
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.nodes: List[OSMNode] = nodes
|
self.inners = inners
|
||||||
self.path = path
|
self.outers = outers
|
||||||
self.style: Dict[str, Any] = style
|
self.style: Dict[str, Any] = style
|
||||||
self.layer = layer
|
self.layer = layer
|
||||||
self.priority = priority
|
|
||||||
self.levels = levels
|
self.levels = levels
|
||||||
|
|
||||||
|
def get_path(
|
||||||
|
self, flinger: Flinger, shift: np.array = np.array((0, 0))) -> str:
|
||||||
|
"""
|
||||||
|
Get SVG path commands.
|
||||||
|
|
||||||
def get_float(string):
|
:param shift: shift vector
|
||||||
"""
|
"""
|
||||||
Try to parse float from a string.
|
path: str = ""
|
||||||
"""
|
|
||||||
try:
|
for outer_nodes in self.outers:
|
||||||
return float(string)
|
path += get_path(
|
||||||
except ValueError:
|
make_counter_clockwise(outer_nodes), shift, flinger) + " "
|
||||||
return 0
|
|
||||||
|
for inner_nodes in self.inners:
|
||||||
|
path += get_path(
|
||||||
|
make_clockwise(inner_nodes), shift, flinger) + " "
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def line_center(nodes: List[OSMNode], flinger: GeoFlinger) -> np.array:
|
def line_center(nodes: List[OSMNode], flinger: Flinger) -> np.array:
|
||||||
"""
|
"""
|
||||||
Get geometric center of nodes set.
|
Get geometric center of nodes set.
|
||||||
|
|
||||||
:param nodes: node list
|
:param nodes: node list
|
||||||
:param flinger: flinger that remap geo positions
|
:param flinger: flinger that remap geo positions
|
||||||
"""
|
"""
|
||||||
x, y = MinMax(), MinMax()
|
boundary = [MinMax(), MinMax()]
|
||||||
|
|
||||||
for node in nodes: # type: OSMNode
|
for node in nodes: # type: OSMNode
|
||||||
flung = flinger.fling(node.position)
|
boundary[0].update(node.position[0])
|
||||||
x.update(flung[0])
|
boundary[1].update(node.position[1])
|
||||||
y.update(flung[1])
|
center_coordinates = np.array((boundary[0].center(), boundary[1].center()))
|
||||||
return np.array(((x.min_ + x.max_) / 2.0, (y.min_ + y.max_) / 2.0))
|
|
||||||
|
return flinger.fling(center_coordinates), center_coordinates
|
||||||
|
|
||||||
|
|
||||||
def get_user_color(text: str, seed: str):
|
def get_user_color(text: str, seed: str):
|
||||||
|
@ -158,7 +192,7 @@ def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_path(nodes: List[OSMNode], shift: np.array, flinger: GeoFlinger) -> str:
|
def get_path(nodes: List[OSMNode], shift: np.array, flinger: Flinger) -> str:
|
||||||
"""
|
"""
|
||||||
Construct SVG path from nodes.
|
Construct SVG path from nodes.
|
||||||
"""
|
"""
|
||||||
|
@ -179,12 +213,15 @@ class Constructor:
|
||||||
"""
|
"""
|
||||||
Röntgen node and way constructor.
|
Röntgen node and way constructor.
|
||||||
"""
|
"""
|
||||||
def __init__(self, check_level, mode, seed, map_, flinger, scheme: Scheme):
|
def __init__(
|
||||||
|
self, check_level, mode, seed, map_, flinger: Flinger,
|
||||||
|
scheme: Scheme):
|
||||||
|
|
||||||
self.check_level = check_level
|
self.check_level = check_level
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.seed = seed
|
self.seed = seed
|
||||||
self.map_ = map_
|
self.map_ = map_
|
||||||
self.flinger = flinger
|
self.flinger: Flinger = flinger
|
||||||
self.scheme: Scheme = scheme
|
self.scheme: Scheme = scheme
|
||||||
|
|
||||||
self.nodes: List[Node] = []
|
self.nodes: List[Node] = []
|
||||||
|
@ -194,44 +231,49 @@ class Constructor:
|
||||||
"""
|
"""
|
||||||
Construct Röntgen ways.
|
Construct Röntgen ways.
|
||||||
"""
|
"""
|
||||||
|
way_number: int = 0
|
||||||
for way_id in self.map_.way_map: # type: int
|
for way_id in self.map_.way_map: # type: int
|
||||||
|
ui.progress_bar(
|
||||||
|
way_number, len(self.map_.way_map),
|
||||||
|
text="Constructing ways")
|
||||||
|
way_number += 1
|
||||||
way: OSMWay = self.map_.way_map[way_id]
|
way: OSMWay = self.map_.way_map[way_id]
|
||||||
if not self.check_level(way.tags):
|
if not self.check_level(way.tags):
|
||||||
continue
|
continue
|
||||||
self.construct_way(way, way.tags, None)
|
self.construct_way(way, way.tags, [], [way.nodes])
|
||||||
|
|
||||||
|
ui.progress_bar(-1, len(self.map_.way_map), text="Constructing ways")
|
||||||
|
|
||||||
def construct_way(
|
def construct_way(
|
||||||
self, way: Optional[OSMWay], tags: Dict[str, Any],
|
self, way: Optional[OSMWay], tags: Dict[str, Any],
|
||||||
path: Optional[str]) -> None:
|
inners, outers) -> None:
|
||||||
"""
|
"""
|
||||||
Way construction.
|
Way construction.
|
||||||
|
|
||||||
:param way: OSM way
|
:param way: OSM way
|
||||||
:param tags: way tag dictionary
|
:param tags: way tag dictionary
|
||||||
:param path: way path (if there is no nodes)
|
|
||||||
"""
|
"""
|
||||||
assert way or path
|
|
||||||
|
|
||||||
layer: float = 0
|
layer: float = 0
|
||||||
level: float = 0
|
# level: float = 0
|
||||||
|
#
|
||||||
|
# if "layer" in tags:
|
||||||
|
# layer = get_float(tags["layer"])
|
||||||
|
# if "level" in tags:
|
||||||
|
# try:
|
||||||
|
# levels = list(map(float, tags["level"].split(";")))
|
||||||
|
# level = sum(levels) / len(levels)
|
||||||
|
# except ValueError:
|
||||||
|
# pass
|
||||||
|
|
||||||
if "layer" in tags:
|
# layer = 100 * level + 0.01 * layer
|
||||||
layer = get_float(tags["layer"])
|
|
||||||
if "level" in tags:
|
|
||||||
try:
|
|
||||||
levels = list(map(float, tags["level"].split(";")))
|
|
||||||
level = sum(levels) / len(levels)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
layer = 100 * level + 0.01 * layer
|
|
||||||
|
|
||||||
nodes = None
|
nodes = None
|
||||||
|
|
||||||
center_point = None
|
center_point, center_coordinates = None, None
|
||||||
|
|
||||||
if way:
|
if way:
|
||||||
center_point = line_center(way.nodes, self.flinger)
|
center_point, center_coordinates = \
|
||||||
|
line_center(way.nodes, self.flinger)
|
||||||
nodes = way.nodes
|
nodes = way.nodes
|
||||||
|
|
||||||
if self.mode == "user-coloring":
|
if self.mode == "user-coloring":
|
||||||
|
@ -239,7 +281,7 @@ class Constructor:
|
||||||
return
|
return
|
||||||
user_color = get_user_color(way.user, self.seed)
|
user_color = get_user_color(way.user, self.seed)
|
||||||
self.ways.append(
|
self.ways.append(
|
||||||
Way("way", nodes, path,
|
Way("way", inners, outers,
|
||||||
{"fill": "none", "stroke": user_color,
|
{"fill": "none", "stroke": user_color,
|
||||||
"stroke-width": 1}))
|
"stroke-width": 1}))
|
||||||
return
|
return
|
||||||
|
@ -249,7 +291,7 @@ class Constructor:
|
||||||
return
|
return
|
||||||
time_color = get_time_color(way.timestamp)
|
time_color = get_time_color(way.timestamp)
|
||||||
self.ways.append(
|
self.ways.append(
|
||||||
Way("way", nodes, path,
|
Way("way", inners, outers,
|
||||||
{"fill": "none", "stroke": time_color,
|
{"fill": "none", "stroke": time_color,
|
||||||
"stroke-width": 1}))
|
"stroke-width": 1}))
|
||||||
return
|
return
|
||||||
|
@ -261,7 +303,7 @@ class Constructor:
|
||||||
kind: str = "way"
|
kind: str = "way"
|
||||||
levels = None
|
levels = None
|
||||||
|
|
||||||
if "building" in tags:
|
if "building" in tags: # or "building:part" in tags:
|
||||||
kind = "building"
|
kind = "building"
|
||||||
if "building:levels" in tags:
|
if "building:levels" in tags:
|
||||||
try:
|
try:
|
||||||
|
@ -288,21 +330,31 @@ class Constructor:
|
||||||
break
|
break
|
||||||
if matched:
|
if matched:
|
||||||
style: Dict[str, Any] = {"fill": "none"}
|
style: Dict[str, Any] = {"fill": "none"}
|
||||||
if "layer" in element:
|
if "priority" in element:
|
||||||
layer += element["layer"]
|
layer = element["priority"]
|
||||||
for key in element: # type: str
|
for key in element: # type: str
|
||||||
if key not in ["tags", "no_tags", "layer", "level", "icon"]:
|
if key not in ["tags", "no_tags", "priority", "level", "icon", "r", "r2"]:
|
||||||
value = element[key]
|
value = element[key]
|
||||||
if isinstance(value, str) and value.endswith("_color"):
|
if isinstance(value, str) and value.endswith("_color"):
|
||||||
value = self.scheme.get_color(value)
|
value = self.scheme.get_color(value)
|
||||||
style[key] = value
|
style[key] = value
|
||||||
|
if center_coordinates is not None:
|
||||||
|
if "r" in element:
|
||||||
|
style["stroke-width"] = \
|
||||||
|
element["r"] * \
|
||||||
|
self.flinger.get_scale(center_coordinates)
|
||||||
|
if "r2" in element:
|
||||||
|
style["stroke-width"] = \
|
||||||
|
element["r2"] * \
|
||||||
|
self.flinger.get_scale(center_coordinates) + 2
|
||||||
self.ways.append(
|
self.ways.append(
|
||||||
Way(kind, nodes, path, style, layer, 50, levels))
|
Way(kind, inners, outers, style, layer, levels))
|
||||||
if center_point is not None and \
|
if center_point is not None and \
|
||||||
(way.is_cycle() or "area" in tags and tags["area"]):
|
(way.is_cycle() or "area" in tags and tags["area"]):
|
||||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||||
self.nodes.append(Node(
|
self.nodes.append(Node(
|
||||||
icon_set, tags, center_point, path, is_for_node=False))
|
icon_set, tags, center_point, center_coordinates,
|
||||||
|
is_for_node=False))
|
||||||
appended = True
|
appended = True
|
||||||
|
|
||||||
if not appended:
|
if not appended:
|
||||||
|
@ -310,12 +362,13 @@ class Constructor:
|
||||||
style: Dict[str, Any] = {
|
style: Dict[str, Any] = {
|
||||||
"fill": "none", "stroke": "#FF0000", "stroke-width": 1}
|
"fill": "none", "stroke": "#FF0000", "stroke-width": 1}
|
||||||
self.ways.append(Way(
|
self.ways.append(Way(
|
||||||
kind, nodes, path, style, layer, 50, levels))
|
kind, inners, outers, style, layer, levels))
|
||||||
if center_point is not None and way.is_cycle() or \
|
if center_point is not None and (way.is_cycle() or
|
||||||
"area" in tags and tags["area"]:
|
"area" in tags and tags["area"]):
|
||||||
icon_set: IconSet = self.scheme.get_icon(tags)
|
icon_set: IconSet = self.scheme.get_icon(tags)
|
||||||
self.nodes.append(Node(
|
self.nodes.append(Node(
|
||||||
icon_set, tags, center_point, path, is_for_node=False))
|
icon_set, tags, center_point, center_coordinates,
|
||||||
|
is_for_node=False))
|
||||||
|
|
||||||
def construct_relations(self) -> None:
|
def construct_relations(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -327,45 +380,35 @@ class Constructor:
|
||||||
if not self.check_level(tags):
|
if not self.check_level(tags):
|
||||||
continue
|
continue
|
||||||
if "type" in tags and tags["type"] == "multipolygon":
|
if "type" in tags and tags["type"] == "multipolygon":
|
||||||
inners, outers = [], []
|
inner_ways: List[OSMWay] = []
|
||||||
|
outer_ways: List[OSMWay] = []
|
||||||
for member in relation.members: # type: OSMMember
|
for member in relation.members: # type: OSMMember
|
||||||
if member.type_ == "way":
|
if member.type_ == "way":
|
||||||
if member.role == "inner":
|
if member.role == "inner":
|
||||||
if member.ref in self.map_.way_map:
|
if member.ref in self.map_.way_map:
|
||||||
inners.append(self.map_.way_map[member.ref])
|
inner_ways.append(self.map_.way_map[member.ref])
|
||||||
elif member.role == "outer":
|
elif member.role == "outer":
|
||||||
if member.ref in self.map_.way_map:
|
if member.ref in self.map_.way_map:
|
||||||
outers.append(self.map_.way_map[member.ref])
|
outer_ways.append(self.map_.way_map[member.ref])
|
||||||
p = ""
|
inners_path: List[List[OSMNode]] = glue(inner_ways)
|
||||||
inners_path = glue(inners)
|
outers_path: List[List[OSMNode]] = glue(outer_ways)
|
||||||
outers_path = glue(outers)
|
self.construct_way(None, tags, inners_path, outers_path)
|
||||||
for nodes in outers_path:
|
|
||||||
path = get_path(nodes, np.array([0, 0]), self.flinger)
|
|
||||||
p += path + " "
|
|
||||||
for nodes in inners_path:
|
|
||||||
nodes.reverse()
|
|
||||||
path = get_path(nodes, np.array([0, 0]), self.flinger)
|
|
||||||
p += path + " "
|
|
||||||
if p:
|
|
||||||
self.construct_way(None, tags, p)
|
|
||||||
|
|
||||||
def construct_nodes(self) -> None:
|
def construct_nodes(self) -> None:
|
||||||
"""
|
"""
|
||||||
Draw nodes.
|
Draw nodes.
|
||||||
"""
|
"""
|
||||||
print("Draw nodes...")
|
|
||||||
|
|
||||||
start_time = datetime.now()
|
|
||||||
|
|
||||||
node_number: int = 0
|
node_number: int = 0
|
||||||
|
|
||||||
s = sorted(
|
s = sorted(
|
||||||
self.map_.node_map.keys(),
|
self.map_.node_map.keys(),
|
||||||
key=lambda x: -self.map_.node_map[x].position.lat)
|
key=lambda x: -self.map_.node_map[x].position[0])
|
||||||
|
|
||||||
for node_id in s: # type: int
|
for node_id in s: # type: int
|
||||||
node_number += 1
|
node_number += 1
|
||||||
ui.progress_bar(node_number, len(self.map_.node_map))
|
ui.progress_bar(
|
||||||
|
node_number, len(self.map_.node_map),
|
||||||
|
text="Constructing nodes")
|
||||||
node: OSMNode = self.map_.node_map[node_id]
|
node: OSMNode = self.map_.node_map[node_id]
|
||||||
flung = self.flinger.fling(node.position)
|
flung = self.flinger.fling(node.position)
|
||||||
tags = node.tags
|
tags = node.tags
|
||||||
|
@ -385,8 +428,6 @@ class Constructor:
|
||||||
if self.mode == "time":
|
if self.mode == "time":
|
||||||
icon_set.color = get_time_color(node.timestamp)
|
icon_set.color = get_time_color(node.timestamp)
|
||||||
|
|
||||||
self.nodes.append(Node(icon_set, tags, flung, None))
|
self.nodes.append(Node(icon_set, tags, flung, node.position))
|
||||||
|
|
||||||
ui.progress_bar(-1, len(self.map_.node_map))
|
ui.progress_bar(-1, len(self.map_.node_map), text="Constructing nodes")
|
||||||
|
|
||||||
print("Nodes painted in " + str(datetime.now() - start_time) + ".")
|
|
||||||
|
|
|
@ -1,114 +1,56 @@
|
||||||
"""
|
"""
|
||||||
Author: Sergey Vartanov (me@enzet.ru)
|
Author: Sergey Vartanov (me@enzet.ru)
|
||||||
"""
|
"""
|
||||||
import math
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import Optional
|
|
||||||
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
|
||||||
def get_ratio(maximum, minimum, ratio: float = 1):
|
EQUATOR_LENGTH: float = 40_075_017
|
||||||
return (maximum[0] - minimum[0]) * ratio / (maximum[1] - minimum[1])
|
|
||||||
|
|
||||||
|
|
||||||
def map_(
|
def pseudo_mercator(coordinates: np.array) -> np.array:
|
||||||
value: float, current_min: float, current_max: float, target_min: float,
|
|
||||||
target_max: float):
|
|
||||||
"""
|
"""
|
||||||
Map current value in bounds of current_min and current_max to bounds of
|
Use spherical pseudo-Mercator projection to convert geo coordinates into
|
||||||
target_min and target_max.
|
plane.
|
||||||
|
|
||||||
|
:param coordinates: geo positional in the form of (latitude, longitude)
|
||||||
|
:return: position on the plane in the form of (x, y)
|
||||||
"""
|
"""
|
||||||
return \
|
return np.array((coordinates[1], 180 / np.pi * np.log(
|
||||||
target_min + (value - current_min) / (current_max - current_min) * \
|
np.tan(np.pi / 4 + coordinates[0] * (np.pi / 180) / 2))))
|
||||||
(target_max - target_min)
|
|
||||||
|
|
||||||
|
|
||||||
class Geo:
|
class Flinger:
|
||||||
def __init__(self, lat: float, lon: float):
|
"""
|
||||||
self.lat: float = lat
|
Convert geo coordinates into SVG position points.
|
||||||
self.lon: float = lon
|
"""
|
||||||
|
def __init__(self, geo_boundaries: MinMax, ratio: float = 1000):
|
||||||
def __getitem__(self, item) -> Optional[float]:
|
|
||||||
if item == 0:
|
|
||||||
return self.lon
|
|
||||||
if item == 1:
|
|
||||||
return self.lat
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __add__(self, other: "Geo") -> "Geo":
|
|
||||||
return Geo(self.lat + other.lat, self.lon + other.lon)
|
|
||||||
|
|
||||||
def __sub__(self, other: "Geo") -> "Geo":
|
|
||||||
return Geo(self.lat - other.lat, self.lon - other.lon)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"{self.lat}, {self.lon}"
|
|
||||||
|
|
||||||
|
|
||||||
class GeoFlinger:
|
|
||||||
def __init__(
|
|
||||||
self, minimum, maximum, target_minimum, target_maximum):
|
|
||||||
"""
|
"""
|
||||||
:param minimum: minimum latitude and longitude
|
:param geo_boundaries: minimum and maximum latitude and longitude
|
||||||
:param maximum: maximum latitude and longitude
|
|
||||||
:param target_minimum: minimum of the resulting image
|
|
||||||
:param target_maximum: maximum of the resulting image
|
|
||||||
"""
|
"""
|
||||||
self.minimum = minimum
|
self.geo_boundaries: MinMax = geo_boundaries
|
||||||
self.maximum = maximum
|
self.ratio: float = ratio
|
||||||
|
self.size: np.array = self.ratio * (
|
||||||
|
pseudo_mercator(self.geo_boundaries.max_) -
|
||||||
|
pseudo_mercator(self.geo_boundaries.min_))
|
||||||
|
self.pixels_per_meter = 360 / EQUATOR_LENGTH * self.ratio
|
||||||
|
|
||||||
# Ratio is depended of latitude. It is always <= 1. In one latitude
|
self.size: np.array = self.size.astype(int).astype(float)
|
||||||
# degree is always 40 000 / 360 km. In one current longitude degree is
|
|
||||||
# about 40 000 / 360 * ratio km.
|
|
||||||
|
|
||||||
ratio = math.sin(
|
def fling(self, coordinates: np.array) -> np.array:
|
||||||
(90.0 - ((self.maximum.lat + self.minimum.lat) / 2.0))
|
|
||||||
/ 180.0 * math.pi)
|
|
||||||
|
|
||||||
# Longitude displayed as x.
|
|
||||||
# Latitude displayed as y.
|
|
||||||
|
|
||||||
# Ratio is x / y.
|
|
||||||
|
|
||||||
space: np.array = [0, 0]
|
|
||||||
current_ratio = get_ratio(self.maximum, self.minimum, ratio)
|
|
||||||
target_ratio = get_ratio(target_maximum, target_minimum)
|
|
||||||
|
|
||||||
if current_ratio >= target_ratio:
|
|
||||||
n = (target_maximum[0] - target_minimum[0]) / \
|
|
||||||
(maximum.lon - minimum.lon) / ratio
|
|
||||||
space[1] = \
|
|
||||||
((target_maximum[1] - target_minimum[1]) -
|
|
||||||
(maximum.lat - minimum.lat) * n) / 2.0
|
|
||||||
space[0] = 0
|
|
||||||
else:
|
|
||||||
n = (target_maximum[1] - target_minimum[1]) / \
|
|
||||||
(maximum.lat - minimum.lat) * ratio
|
|
||||||
space[0] = \
|
|
||||||
((target_maximum[0] - target_minimum[0]) -
|
|
||||||
(maximum.lon - minimum.lon) * n) / 2.0
|
|
||||||
space[1] = 0
|
|
||||||
|
|
||||||
self.target_minimum = np.add(target_minimum, space)
|
|
||||||
self.target_maximum = np.subtract(target_maximum, space)
|
|
||||||
|
|
||||||
meters_per_pixel = \
|
|
||||||
(self.maximum.lat - self.minimum.lat) / \
|
|
||||||
(self.target_maximum[1] - self.target_minimum[1]) * \
|
|
||||||
40000 / 360 * 1000
|
|
||||||
|
|
||||||
self.scale = 1 / meters_per_pixel
|
|
||||||
|
|
||||||
self.space = space
|
|
||||||
|
|
||||||
def fling(self, current) -> np.array:
|
|
||||||
"""
|
"""
|
||||||
:param current: vector to fling
|
:param coordinates: vector to fling
|
||||||
"""
|
"""
|
||||||
x = map_(
|
result: np.array = self.ratio * (
|
||||||
current.lon, self.minimum.lon, self.maximum.lon,
|
pseudo_mercator(coordinates) -
|
||||||
self.target_minimum[0], self.target_maximum[0])
|
pseudo_mercator(self.geo_boundaries.min_))
|
||||||
y = map_(
|
|
||||||
self.maximum.lat + self.minimum.lat - current.lat,
|
# Invert y axis on coordinate plane.
|
||||||
self.minimum.lat, self.maximum.lat,
|
result[1] = self.size[1] - result[1]
|
||||||
self.target_minimum[1], self.target_maximum[1])
|
|
||||||
return np.array([x, y])
|
return result
|
||||||
|
|
||||||
|
def get_scale(self, coordinates: np.array) -> float:
|
||||||
|
scale_factor = 1 / np.cos(coordinates[0] / 180 * np.pi)
|
||||||
|
return self.pixels_per_meter * scale_factor
|
||||||
|
|
|
@ -16,14 +16,15 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
from roentgen.address import get_address
|
from roentgen.address import get_address
|
||||||
from roentgen.constructor import Constructor, get_path, Node, Way
|
from roentgen.constructor import Constructor, Node, Way
|
||||||
from roentgen.flinger import GeoFlinger, Geo
|
from roentgen.flinger import Flinger
|
||||||
from roentgen.grid import draw_grid
|
from roentgen.grid import draw_grid
|
||||||
from roentgen.extract_icon import Icon, IconExtractor
|
from roentgen.extract_icon import Icon, IconExtractor
|
||||||
from roentgen.osm_getter import get_osm
|
from roentgen.osm_getter import get_osm
|
||||||
from roentgen.osm_reader import Map, OSMReader
|
from roentgen.osm_reader import Map, OSMReader
|
||||||
from roentgen.scheme import Scheme
|
from roentgen.scheme import Scheme
|
||||||
from roentgen.direction import DirectionSet, Sector
|
from roentgen.direction import DirectionSet, Sector
|
||||||
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
ICONS_FILE_NAME: str = "icons/icons.svg"
|
ICONS_FILE_NAME: str = "icons/icons.svg"
|
||||||
TAGS_FILE_NAME: str = "data/tags.yml"
|
TAGS_FILE_NAME: str = "data/tags.yml"
|
||||||
|
@ -40,7 +41,7 @@ class Painter:
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, show_missing_tags: bool, overlap: int, draw_nodes: bool,
|
self, show_missing_tags: bool, overlap: int, draw_nodes: bool,
|
||||||
mode: str, draw_captions: str, map_: Map, flinger: GeoFlinger,
|
mode: str, draw_captions: str, map_: Map, flinger: Flinger,
|
||||||
svg: svgwrite.Drawing, icon_extractor: IconExtractor,
|
svg: svgwrite.Drawing, icon_extractor: IconExtractor,
|
||||||
scheme: Scheme):
|
scheme: Scheme):
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ class Painter:
|
||||||
self.draw_captions: str = draw_captions
|
self.draw_captions: str = draw_captions
|
||||||
|
|
||||||
self.map_: Map = map_
|
self.map_: Map = map_
|
||||||
self.flinger: GeoFlinger = flinger
|
self.flinger: Flinger = flinger
|
||||||
self.svg: svgwrite.Drawing = svg
|
self.svg: svgwrite.Drawing = svg
|
||||||
self.icon_extractor = icon_extractor
|
self.icon_extractor = icon_extractor
|
||||||
self.scheme: Scheme = scheme
|
self.scheme: Scheme = scheme
|
||||||
|
@ -241,54 +242,48 @@ class Painter:
|
||||||
else:
|
else:
|
||||||
shift_2 = [0, -3]
|
shift_2 = [0, -3]
|
||||||
|
|
||||||
if way.nodes:
|
for nodes in way.inners + way.outers:
|
||||||
for i in range(len(way.nodes) - 1):
|
for i in range(len(nodes) - 1):
|
||||||
flung_1 = self.flinger.fling(way.nodes[i].position)
|
flung_1 = self.flinger.fling(nodes[i].position)
|
||||||
flung_2 = self.flinger.fling(way.nodes[i + 1].position)
|
flung_2 = self.flinger.fling(nodes[i + 1].position)
|
||||||
|
|
||||||
self.svg.add(self.svg.path(
|
self.svg.add(self.svg.path(
|
||||||
d=("M", np.add(flung_1, shift_1), "L",
|
d=("M", np.add(flung_1, shift_1), "L",
|
||||||
np.add(flung_2, shift_1), np.add(flung_2, shift_2),
|
np.add(flung_2, shift_1), np.add(flung_2, shift_2),
|
||||||
np.add(flung_1, shift_2), "Z"),
|
np.add(flung_1, shift_2), "Z"),
|
||||||
fill=color, stroke=color, stroke_width=1))
|
fill=color, stroke=color, stroke_width=1))
|
||||||
elif way.path:
|
|
||||||
# TODO: implement
|
|
||||||
pass
|
|
||||||
|
|
||||||
def draw(self, nodes, ways, points):
|
def draw(self, nodes: List[Node], ways: List[Way], points):
|
||||||
"""
|
"""
|
||||||
Draw map.
|
Draw map.
|
||||||
"""
|
"""
|
||||||
ways = sorted(ways, key=lambda x: x.layer)
|
ways = sorted(ways, key=lambda x: x.layer)
|
||||||
for way in ways:
|
for way in ways: # type: Way
|
||||||
if way.kind == "way":
|
if way.kind == "way":
|
||||||
if way.nodes:
|
path: str = way.get_path(self.flinger)
|
||||||
path = get_path(way.nodes, np.array([0, 0]), self.flinger)
|
if path:
|
||||||
p = Path(d=path)
|
p = Path(d=path)
|
||||||
p.update(way.style)
|
p.update(way.style)
|
||||||
self.svg.add(p)
|
self.svg.add(p)
|
||||||
else:
|
|
||||||
p = Path(d=way.path)
|
|
||||||
p.update(way.style)
|
|
||||||
self.svg.add(p)
|
|
||||||
|
|
||||||
# Building shade
|
# Building shade
|
||||||
|
|
||||||
building_shade = Group(opacity=0.1)
|
building_shade = Group(opacity=0.1)
|
||||||
|
|
||||||
for way in ways: # type: Way
|
for way in ways: # type: Way
|
||||||
if way.kind != "building" or not way.nodes:
|
if way.kind != "building":
|
||||||
continue
|
continue
|
||||||
shift = [-5, 5]
|
shift = [-5, 5]
|
||||||
if way.levels:
|
if way.levels:
|
||||||
shift = [-5 * way.levels, 5 * way.levels]
|
shift = [-5 * way.levels, 5 * way.levels]
|
||||||
for i in range(len(way.nodes) - 1):
|
for nodes11 in way.inners + way.outers:
|
||||||
flung_1 = self.flinger.fling(way.nodes[i].position)
|
for i in range(len(nodes11) - 1):
|
||||||
flung_2 = self.flinger.fling(way.nodes[i + 1].position)
|
flung_1 = self.flinger.fling(nodes11[i].position)
|
||||||
building_shade.add(Path(
|
flung_2 = self.flinger.fling(nodes11[i + 1].position)
|
||||||
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
|
building_shade.add(Path(
|
||||||
np.add(flung_1, shift), "Z"),
|
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
|
||||||
fill="#000000", stroke="#000000", stroke_width=1))
|
np.add(flung_1, shift), "Z"),
|
||||||
|
fill="#000000", stroke="#000000", stroke_width=1))
|
||||||
|
|
||||||
self.svg.add(building_shade)
|
self.svg.add(building_shade)
|
||||||
|
|
||||||
|
@ -300,21 +295,28 @@ class Painter:
|
||||||
|
|
||||||
# Building roof
|
# Building roof
|
||||||
|
|
||||||
|
building_paths: List[(str, Dict)] = []
|
||||||
|
|
||||||
for way in ways: # type: Way
|
for way in ways: # type: Way
|
||||||
if way.kind != "building":
|
if way.kind != "building":
|
||||||
continue
|
continue
|
||||||
if way.nodes:
|
shift = [0, -3]
|
||||||
shift = [0, -3]
|
if way.levels:
|
||||||
if way.levels:
|
shift = np.array([0 * way.levels, min(-3, -1 * way.levels)])
|
||||||
shift = np.array([0 * way.levels, min(-3, -1 * way.levels)])
|
path: str = way.get_path(self.flinger, shift)
|
||||||
path = get_path(way.nodes, shift, self.flinger)
|
if path:
|
||||||
p = Path(d=path, opacity=1)
|
building_paths.append((path, way.style))
|
||||||
p.update(way.style)
|
|
||||||
self.svg.add(p)
|
for path, style in building_paths:
|
||||||
else:
|
p = Path(d=path, opacity=1)
|
||||||
p = Path(d=way.path, opacity=1)
|
p.update(style)
|
||||||
p.update(way.style)
|
p.update({"stroke": "none"})
|
||||||
self.svg.add(p)
|
self.svg.add(p)
|
||||||
|
for path, style in building_paths:
|
||||||
|
p = Path(d=path, opacity=1)
|
||||||
|
p.update(style)
|
||||||
|
p.update({"fill": "none"})
|
||||||
|
self.svg.add(p)
|
||||||
|
|
||||||
# Trees
|
# Trees
|
||||||
|
|
||||||
|
@ -326,12 +328,14 @@ class Painter:
|
||||||
if "circumference" in node.tags:
|
if "circumference" in node.tags:
|
||||||
self.svg.add(self.svg.circle(
|
self.svg.add(self.svg.circle(
|
||||||
node.point,
|
node.point,
|
||||||
float(node.tags["circumference"]) * self.flinger.scale / 2,
|
float(node.tags["circumference"]) *
|
||||||
|
self.flinger.get_scale(node.coordinates) / 2,
|
||||||
fill="#AAAA88", opacity=0.3))
|
fill="#AAAA88", opacity=0.3))
|
||||||
if "diameter_crown" in node.tags:
|
if "diameter_crown" in node.tags:
|
||||||
self.svg.add(self.svg.circle(
|
self.svg.add(self.svg.circle(
|
||||||
node.point,
|
node.point,
|
||||||
float(node.tags["diameter_crown"]) * self.flinger.scale / 2,
|
float(node.tags["diameter_crown"]) *
|
||||||
|
self.flinger.get_scale(node.coordinates) / 2,
|
||||||
fill=self.scheme.get_color("evergreen"), opacity=0.3))
|
fill=self.scheme.get_color("evergreen"), opacity=0.3))
|
||||||
|
|
||||||
# Directions
|
# Directions
|
||||||
|
@ -347,16 +351,19 @@ class Painter:
|
||||||
angle = float(node.get_tag("camera:angle"))
|
angle = float(node.get_tag("camera:angle"))
|
||||||
if "angle" in node.tags:
|
if "angle" in node.tags:
|
||||||
angle = float(node.get_tag("angle"))
|
angle = float(node.get_tag("angle"))
|
||||||
direction_radius: int = 25 * self.flinger.scale
|
direction_radius: float = \
|
||||||
|
25 * self.flinger.get_scale(node.coordinates)
|
||||||
direction_color: str = \
|
direction_color: str = \
|
||||||
self.scheme.get_color("direction_camera_color")
|
self.scheme.get_color("direction_camera_color")
|
||||||
elif node.get_tag("traffic_sign") == "stop":
|
elif node.get_tag("traffic_sign") == "stop":
|
||||||
direction = node.get_tag("direction")
|
direction = node.get_tag("direction")
|
||||||
direction_radius: int = 25 * self.flinger.scale
|
direction_radius: float = \
|
||||||
|
25 * self.flinger.get_scale(node.coordinates)
|
||||||
direction_color: str = "#FF0000"
|
direction_color: str = "#FF0000"
|
||||||
else:
|
else:
|
||||||
direction = node.get_tag("direction")
|
direction = node.get_tag("direction")
|
||||||
direction_radius: int = 100 * self.flinger.scale
|
direction_radius: float = \
|
||||||
|
50 * self.flinger.get_scale(node.coordinates)
|
||||||
direction_color: str = \
|
direction_color: str = \
|
||||||
self.scheme.get_color("direction_view_color")
|
self.scheme.get_color("direction_view_color")
|
||||||
is_revert_gradient = True
|
is_revert_gradient = True
|
||||||
|
@ -367,11 +374,9 @@ class Painter:
|
||||||
point = (node.point.astype(int)).astype(float)
|
point = (node.point.astype(int)).astype(float)
|
||||||
|
|
||||||
if angle:
|
if angle:
|
||||||
paths = [Sector(direction, angle)
|
paths = [Sector(direction, angle).draw(point, direction_radius)]
|
||||||
.draw(point, direction_radius)]
|
|
||||||
else:
|
else:
|
||||||
paths = DirectionSet(direction) \
|
paths = DirectionSet(direction).draw(point, direction_radius)
|
||||||
.draw(point, direction_radius)
|
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
gradient = self.svg.defs.add(self.svg.radialGradient(
|
gradient = self.svg.defs.add(self.svg.radialGradient(
|
||||||
|
@ -397,9 +402,9 @@ class Painter:
|
||||||
("diameter_crown" in node.tags or
|
("diameter_crown" in node.tags or
|
||||||
"circumference" in node.tags):
|
"circumference" in node.tags):
|
||||||
continue
|
continue
|
||||||
ui.progress_bar(index, len(nodes), step=10)
|
ui.progress_bar(index, len(nodes), step=10, text="Draw nodes")
|
||||||
self.draw_shapes(node, points)
|
self.draw_shapes(node, points)
|
||||||
ui.progress_bar(-1, len(nodes), step=10)
|
ui.progress_bar(-1, len(nodes), step=10, text="Draw nodes")
|
||||||
|
|
||||||
if self.draw_captions == "no":
|
if self.draw_captions == "no":
|
||||||
return
|
return
|
||||||
|
@ -494,13 +499,13 @@ def check_level_overground(tags: Dict[str, Any]):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(argv):
|
||||||
if len(sys.argv) == 2:
|
if len(argv) == 2:
|
||||||
if sys.argv[1] == "grid":
|
if argv[1] == "grid":
|
||||||
draw_grid()
|
draw_grid()
|
||||||
return
|
return
|
||||||
|
|
||||||
options = ui.parse_options(sys.argv)
|
options = ui.parse_options(argv)
|
||||||
|
|
||||||
if not options:
|
if not options:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -538,23 +543,19 @@ def main():
|
||||||
|
|
||||||
map_: Map = osm_reader.map_
|
map_: Map = osm_reader.map_
|
||||||
|
|
||||||
w, h = list(map(lambda x: float(x), options.size.split(",")))
|
|
||||||
|
|
||||||
svg: svgwrite.Drawing = \
|
|
||||||
svgwrite.Drawing(options.output_file_name, size=(w, h))
|
|
||||||
|
|
||||||
svg.add(Rect((0, 0), (w, h), fill=background_color))
|
|
||||||
|
|
||||||
min1 = Geo(boundary_box[1], boundary_box[0])
|
|
||||||
max1 = Geo(boundary_box[3], boundary_box[2])
|
|
||||||
|
|
||||||
missing_tags = {}
|
missing_tags = {}
|
||||||
points = []
|
points = []
|
||||||
|
|
||||||
scheme: Scheme = Scheme(TAGS_FILE_NAME, COLORS_FILE_NAME)
|
scheme: Scheme = Scheme(TAGS_FILE_NAME, COLORS_FILE_NAME)
|
||||||
|
|
||||||
flinger: GeoFlinger = \
|
min1: np.array = np.array((boundary_box[1], boundary_box[0]))
|
||||||
GeoFlinger(min1, max1, np.array([0, 0]), np.array([w, h]))
|
max1: np.array = np.array((boundary_box[3], boundary_box[2]))
|
||||||
|
flinger: Flinger = Flinger(MinMax(min1, max1), options.scale)
|
||||||
|
size: np.array = flinger.size
|
||||||
|
|
||||||
|
svg: svgwrite.Drawing = \
|
||||||
|
svgwrite.Drawing(options.output_file_name, size=size)
|
||||||
|
svg.add(Rect((0, 0), size, fill=background_color))
|
||||||
|
|
||||||
icon_extractor: IconExtractor = IconExtractor(ICONS_FILE_NAME)
|
icon_extractor: IconExtractor = IconExtractor(ICONS_FILE_NAME)
|
||||||
|
|
||||||
|
@ -589,21 +590,12 @@ def main():
|
||||||
scheme=scheme)
|
scheme=scheme)
|
||||||
painter.draw(constructor.nodes, constructor.ways, points)
|
painter.draw(constructor.nodes, constructor.ways, points)
|
||||||
|
|
||||||
if flinger.space[0] == 0:
|
|
||||||
svg.add(Rect((0, 0), (w, flinger.space[1]), fill="#FFFFFF"))
|
|
||||||
svg.add(Rect(
|
|
||||||
(0, h - flinger.space[1]), (w, flinger.space[1]), fill="#FFFFFF"))
|
|
||||||
if flinger.space[1] == 0:
|
|
||||||
svg.add(Rect((0, 0), (flinger.space[0], h), fill="#FFFFFF"))
|
|
||||||
svg.add(Rect(
|
|
||||||
(w - flinger.space[0], 0), (flinger.space[0], h), fill="#FFFFFF"))
|
|
||||||
|
|
||||||
if options.show_index:
|
if options.show_index:
|
||||||
draw_index(flinger, map_, max1, min1, svg)
|
draw_index(flinger, map_, max1, min1, svg)
|
||||||
|
|
||||||
print("Writing output SVG...")
|
print("Writing output SVG...")
|
||||||
svg.write(open(options.output_file_name, "w"))
|
svg.write(open(options.output_file_name, "w"))
|
||||||
print("Done")
|
print("Done.")
|
||||||
|
|
||||||
top_missing_tags = \
|
top_missing_tags = \
|
||||||
sorted(missing_tags.keys(), key=lambda x: -missing_tags[x])
|
sorted(missing_tags.keys(), key=lambda x: -missing_tags[x])
|
||||||
|
@ -615,13 +607,13 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
def draw_index(flinger, map_, max1, min1, svg):
|
def draw_index(flinger, map_, max1, min1, svg):
|
||||||
print(min1.lon, max1.lon)
|
print(min1[1], max1[1])
|
||||||
print(min1.lat, max1.lat)
|
print(min1[0], max1[0])
|
||||||
lon_step = 0.001
|
lon_step = 0.001
|
||||||
lat_step = 0.001
|
lat_step = 0.001
|
||||||
matrix = []
|
matrix = []
|
||||||
lat_number = int((max1.lat - min1.lat) / lat_step) + 1
|
lat_number = int((max1[0] - min1[0]) / lat_step) + 1
|
||||||
lon_number = int((max1.lon - min1.lon) / lon_step) + 1
|
lon_number = int((max1[1] - min1[1]) / lon_step) + 1
|
||||||
for i in range(lat_number):
|
for i in range(lat_number):
|
||||||
row = []
|
row = []
|
||||||
for j in range(lon_number):
|
for j in range(lon_number):
|
||||||
|
@ -629,8 +621,8 @@ def draw_index(flinger, map_, max1, min1, svg):
|
||||||
matrix.append(row)
|
matrix.append(row)
|
||||||
for node_id in map_.node_map: # type: int
|
for node_id in map_.node_map: # type: int
|
||||||
node = map_.node_map[node_id]
|
node = map_.node_map[node_id]
|
||||||
i = int((node.lat - min1.lat) / lat_step)
|
i = int((node[0] - min1[0]) / lat_step)
|
||||||
j = int((node.lon - min1.lon) / lon_step)
|
j = int((node[1] - min1[1]) / lon_step)
|
||||||
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
||||||
matrix[i][j] += 1
|
matrix[i][j] += 1
|
||||||
if "tags" in node:
|
if "tags" in node:
|
||||||
|
@ -640,18 +632,18 @@ def draw_index(flinger, map_, max1, min1, svg):
|
||||||
if "tags" in way:
|
if "tags" in way:
|
||||||
for node_id in way.nodes:
|
for node_id in way.nodes:
|
||||||
node = map_.node_map[node_id]
|
node = map_.node_map[node_id]
|
||||||
i = int((node.lat - min1.lat) / lat_step)
|
i = int((node[0] - min1[0]) / lat_step)
|
||||||
j = int((node.lon - min1.lon) / lon_step)
|
j = int((node[1] - min1[1]) / lon_step)
|
||||||
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
if (0 <= i < lat_number) and (0 <= j < lon_number):
|
||||||
matrix[i][j] += len(way.tags) / float(
|
matrix[i][j] += len(way.tags) / float(
|
||||||
len(way.nodes))
|
len(way.nodes))
|
||||||
for i in range(lat_number):
|
for i in range(lat_number):
|
||||||
for j in range(lon_number):
|
for j in range(lon_number):
|
||||||
t1 = flinger.fling(Geo(
|
t1 = flinger.fling(np.array((
|
||||||
min1.lat + i * lat_step, min1.lon + j * lon_step))
|
min1[0] + i * lat_step, min1[1] + j * lon_step)))
|
||||||
t2 = flinger.fling(Geo(
|
t2 = flinger.fling(np.array((
|
||||||
min1.lat + (i + 1) * lat_step,
|
min1[0] + (i + 1) * lat_step,
|
||||||
min1.lon + (j + 1) * lon_step))
|
min1[1] + (j + 1) * lon_step)))
|
||||||
svg.add(Text(
|
svg.add(Text(
|
||||||
str(int(matrix[i][j])),
|
str(int(matrix[i][j])),
|
||||||
(((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40),
|
(((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40),
|
||||||
|
|
|
@ -3,10 +3,10 @@ Reading OpenStreetMap data from XML file.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
|
import numpy as np
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, List, Optional, Set, Union
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
from roentgen.flinger import Geo
|
|
||||||
from roentgen.ui import progress_bar
|
from roentgen.ui import progress_bar
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class OSMNode:
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.id_: Optional[int] = None
|
self.id_: Optional[int] = None
|
||||||
self.position: Optional[Geo] = None
|
self.position: Optional[np.array] = None
|
||||||
self.tags: Dict[str, str] = {}
|
self.tags: Dict[str, str] = {}
|
||||||
|
|
||||||
self.visible: Optional[str] = None
|
self.visible: Optional[str] = None
|
||||||
|
@ -38,8 +38,8 @@ class OSMNode:
|
||||||
:param is_full: if false, parse only ID, latitude and longitude
|
:param is_full: if false, parse only ID, latitude and longitude
|
||||||
"""
|
"""
|
||||||
self.id_ = int(get_value("id", text))
|
self.id_ = int(get_value("id", text))
|
||||||
self.position = Geo(
|
self.position = np.array((
|
||||||
float(get_value("lat", text)), float(get_value("lon", text)))
|
float(get_value("lat", text)), float(get_value("lon", text))))
|
||||||
|
|
||||||
if is_full:
|
if is_full:
|
||||||
self.visible = get_value("visible", text)
|
self.visible = get_value("visible", text)
|
||||||
|
|
|
@ -77,8 +77,6 @@ class Scheme:
|
||||||
if color.startswith("#"):
|
if color.startswith("#"):
|
||||||
return color
|
return color
|
||||||
|
|
||||||
print(f"No color {color}.")
|
|
||||||
|
|
||||||
return DEFAULT_COLOR
|
return DEFAULT_COLOR
|
||||||
|
|
||||||
def is_no_drawable(self, key: str) -> bool:
|
def is_no_drawable(self, key: str) -> bool:
|
||||||
|
|
|
@ -36,10 +36,11 @@ def parse_options(args):
|
||||||
help="geo boundary box, use \"m\" instead of \"-\" for negative values",
|
help="geo boundary box, use \"m\" instead of \"-\" for negative values",
|
||||||
required=True)
|
required=True)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-s", "--size",
|
"-s", "--scale",
|
||||||
metavar="<width>,<height>",
|
metavar="<float>",
|
||||||
help="output SVG file size in pixels",
|
help="map scale",
|
||||||
dest="size",
|
dest="scale",
|
||||||
|
type=float,
|
||||||
required=True)
|
required=True)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-nn", "--no-draw-nodes",
|
"-nn", "--no-draw-nodes",
|
||||||
|
@ -95,7 +96,8 @@ def parse_options(args):
|
||||||
|
|
||||||
|
|
||||||
def progress_bar(
|
def progress_bar(
|
||||||
number: int, total: int, length: int = 20, step: int = 1000) -> None:
|
number: int, total: int, length: int = 20, step: int = 1000,
|
||||||
|
text: str = "") -> None:
|
||||||
"""
|
"""
|
||||||
Draw progress bar using Unicode symbols.
|
Draw progress bar using Unicode symbols.
|
||||||
|
|
||||||
|
@ -106,7 +108,7 @@ def progress_bar(
|
||||||
subsequently)
|
subsequently)
|
||||||
"""
|
"""
|
||||||
if number == -1:
|
if number == -1:
|
||||||
print(f"100 % {length * '█'}▏")
|
print(f"100 % {length * '█'}▏{text}")
|
||||||
elif number % step == 0:
|
elif number % step == 0:
|
||||||
ratio: float = number / total
|
ratio: float = number / total
|
||||||
parts: int = int(ratio * length * BOXES_LENGTH)
|
parts: int = int(ratio * length * BOXES_LENGTH)
|
||||||
|
@ -114,7 +116,7 @@ def progress_bar(
|
||||||
box: str = BOXES[int(parts - fill_length * BOXES_LENGTH)]
|
box: str = BOXES[int(parts - fill_length * BOXES_LENGTH)]
|
||||||
print(
|
print(
|
||||||
f"{str(int(int(ratio * 1000) / 10)):>3} % {fill_length * '█'}{box}"
|
f"{str(int(int(ratio * 1000) / 10)):>3} % {fill_length * '█'}{box}"
|
||||||
f"{int(length - fill_length - 1) * ' '}▏")
|
f"{int(length - fill_length - 1) * ' '}▏{text}")
|
||||||
sys.stdout.write("\033[F")
|
sys.stdout.write("\033[F")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ class MinMax:
|
||||||
"""
|
"""
|
||||||
Minimum and maximum.
|
Minimum and maximum.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, min_=None, max_=None):
|
||||||
self.min_ = None
|
self.min_ = min_
|
||||||
self.max_ = None
|
self.max_ = max_
|
||||||
|
|
||||||
def update(self, value):
|
def update(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -18,3 +18,6 @@ class MinMax:
|
||||||
Difference between maximum and minimum.
|
Difference between maximum and minimum.
|
||||||
"""
|
"""
|
||||||
return self.max_ - self.min_
|
return self.max_ - self.min_
|
||||||
|
|
||||||
|
def center(self):
|
||||||
|
return (self.min_ + self.max_) / 2
|
||||||
|
|
4
run.py
4
run.py
|
@ -3,7 +3,9 @@ Röntgen entry point.
|
||||||
|
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
from roentgen.mapper import main
|
from roentgen.mapper import main
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main(sys.argv)
|
||||||
|
|
|
@ -2,13 +2,8 @@
|
||||||
Author: Sergey Vartanov (me@enzet.ru).
|
Author: Sergey Vartanov (me@enzet.ru).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from roentgen.flinger import map_
|
|
||||||
from roentgen.grid import draw_grid
|
from roentgen.grid import draw_grid
|
||||||
|
|
||||||
|
|
||||||
def test_flinger_map():
|
|
||||||
assert map_(5, 0, 10, 0, 20) == 10
|
|
||||||
|
|
||||||
|
|
||||||
def test_icons():
|
def test_icons():
|
||||||
draw_grid()
|
draw_grid()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue