Skip to content

Commit

Permalink
Merge pull request #72 from shannonturner/v7_2024
Browse files Browse the repository at this point in the history
v7: Add line styles, mix and match line widths and styles, add redo, more
  • Loading branch information
shannonturner authored Oct 25, 2024
2 parents dfb3b2c + d834ce3 commit 96e1ef2
Show file tree
Hide file tree
Showing 19 changed files with 2,112 additions and 276 deletions.
6 changes: 5 additions & 1 deletion metro_map_saver/map_saver/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
hex64,
validate_metro_map,
validate_metro_map_v2,
validate_metro_map_v3,
)

import hashlib
Expand All @@ -24,7 +25,10 @@ def clean_mapdata(self):
mapdata = self.cleaned_data['mapdata']

data_version = mapdata.get('global', {}).get('data_version', 1)
if data_version == 2:
if data_version == 3:
mapdata = validate_metro_map_v3(mapdata)
mapdata['global']['data_version'] = 3
elif data_version == 2:
mapdata = validate_metro_map_v2(mapdata)
mapdata['global']['data_version'] = 2
else:
Expand Down
89 changes: 85 additions & 4 deletions metro_map_saver/map_saver/mapdata_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,40 @@
</svg>
''')

# For use with data_version >= 3
SVG_TEMPLATE_V3 = Template('''
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {{ canvas_size|default:80 }} {{ canvas_size|default:80 }}">
{% spaceless %}
{% load metromap_utils %}
{% if stations %}
<style>text { font: 1px Helvetica; font-weight: 600; white-space: pre; dominant-baseline: central; } line { stroke-width: {{ line_size|default:1 }}; fill: none; stroke-linecap: round; stroke-linejoin: round; }{% for hex, class_name in color_map.items %} .{{ class_name }} { stroke: #{{ hex }} }{% endfor %} {% get_line_width_styles_for_svg_style shapes_by_color %}</style>
{% get_station_styles_in_use stations default_station_shape line_size %}
{% else %}
<style>line { stroke-width: {{ line_size|default:1 }}; fill: none; stroke-linecap: round; stroke-linejoin: round; }{% for hex, class_name in color_map.items %} .{{ class_name }} { stroke: #{{ hex }} }{% endfor %} {% get_line_width_styles_for_svg_style shapes_by_color %}</style>
{% endif %}
{% for color, line_width_style in shapes_by_color.items %}
{% for width_style, shapes in line_width_style.items %}
{% for line in shapes.lines %}
<line class="{% map_color color color_map %} {% get_line_class_from_width_style width_style line_size %}" x1="{{ line.0 }}" y1="{{ line.1 }}" x2="{{ line.2 }}" y2="{{ line.3 }}"/>
{% endfor %}
{% for point in shapes.points %}
{% if default_station_shape == 'rect' %}
<rect x="{{ point.0|add:-0.5 }}" y="{{ point.1|add:-0.5 }}" w="1" h="1" fill="#{{ color }}" />
{% else %}
<circle cx="{{ point.0 }}" cy="{{ point.1 }}" r="{{ point.size|default:1 }}" fill="#{{ color }}" />
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endspaceless %}
</svg>
''')

STATIONS_SVG_TEMPLATE = Template('''
{% spaceless %}
{% load metromap_utils %}
{% for station in stations %}
{% station_marker station default_station_shape line_size points_by_color stations %}
{% station_marker station default_station_shape line_size points_by_color stations data_version %}
{% station_text station %}
{% endfor %}
{% endspaceless %}
Expand Down Expand Up @@ -73,10 +102,13 @@ def sort_points_by_color(mapdata, map_type='classic', data_version=1):
# Order matters
1: reversed(ALLOWED_MAP_SIZES),
2: reversed(ALLOWED_MAP_SIZES),
3: reversed(ALLOWED_MAP_SIZES),
}
}
allowed_sizes = allowed_sizes[map_type][data_version]

linewidthstyles_by_xy = {}

if map_type == 'classic' and data_version == 1:
# Ex: [0][1]['line']: 'bd1038'
for x in sorted(mapdata):
Expand Down Expand Up @@ -163,6 +195,49 @@ def sort_points_by_color(mapdata, map_type='classic', data_version=1):

points_by_color[line_color]['xy'].add((x, y))

elif map_type == 'classic' and data_version == 3:
colors_by_xy = {}

default_station_shape = mapdata['global'].get('style', {}).get('mapStationStyle', 'wmata')

for line_color in mapdata['points_by_color']:
for width_style in mapdata['points_by_color'][line_color]:
for x in mapdata['points_by_color'][line_color][width_style]:
for y in mapdata['points_by_color'][line_color][width_style][x]:
x = str(x)
y = str(y)
if x not in VALID_XY or y not in VALID_XY:
continue

if mapdata['points_by_color'][line_color][width_style][x][y] != 1:
continue

if line_color not in points_by_color:
points_by_color[line_color] = {
width_style: set(),
}
elif width_style not in points_by_color[line_color]:
points_by_color[line_color][width_style] = set()

colors_by_xy[f'{x},{y}'] = line_color
linewidthstyles_by_xy[f'{x},{y}'] = width_style

x = int(x)
y = int(y)

if x > highest_seen:
highest_seen = x
if y > highest_seen:
highest_seen = y

points_by_color[line_color][width_style].add((x, y))

if map_type == 'classic' and data_version >= 2:

default_line_width = mapdata['global'].get('style', {}).get('mapLineWidth', 1)
default_line_style = mapdata['global'].get('style', {}).get('mapLineStyle', 'solid')
default_line_width_style = f'{default_line_width}-{default_line_style}'

for x in mapdata['stations']:
for y in mapdata['stations'][x]:
if x not in VALID_XY or y not in VALID_XY:
Expand All @@ -174,12 +249,14 @@ def sort_points_by_color(mapdata, map_type='classic', data_version=1):
'orientation': station.get('orientation', 0),
'xy': (int(x), int(y)),
'color': colors_by_xy[f'{x},{y}'],
'line_width_style': linewidthstyles_by_xy.get(f'{x},{y}', default_line_width_style)
}
if station.get('transfer'):
station_data['transfer'] = 1
station_data['style'] = station.get('style', default_station_shape)
stations.append(station_data)


for size in allowed_sizes:
if highest_seen < size:
map_size = size
Expand Down Expand Up @@ -342,7 +419,7 @@ def reduce_straight_line(line):
# Can't be reduced further
return line

def get_svg_from_shapes_by_color(shapes_by_color, map_size, line_size, default_station_shape, points_by_color, stations=False):
def get_svg_from_shapes_by_color(shapes_by_color, map_size, line_size, default_station_shape, points_by_color, stations=False, data_version=3):

""" Finally, let's draw SVG from the sorted shapes by color.
Expand All @@ -365,9 +442,12 @@ def get_svg_from_shapes_by_color(shapes_by_color, map_size, line_size, default_s
'color_map': {color: f'c{index}' for index, color in enumerate(points_by_color.keys())},
}

return SVG_TEMPLATE.render(Context(context))
if data_version >= 3:
return SVG_TEMPLATE_V3.render(Context(context))
else:
return SVG_TEMPLATE.render(Context(context))

def add_stations_to_svg(thumbnail_svg, line_size, default_station_shape, points_by_color, stations):
def add_stations_to_svg(thumbnail_svg, line_size, default_station_shape, points_by_color, stations, data_version):

""" This allows me to avoid generating the map SVG twice
(once for thumbnails, once for stations)
Expand All @@ -383,6 +463,7 @@ def add_stations_to_svg(thumbnail_svg, line_size, default_station_shape, points_
'default_station_shape': default_station_shape,
'points_by_color': points_by_color,
'stations': stations,
"data_version": data_version,
}

return thumbnail_svg.replace('</svg>', STATIONS_SVG_TEMPLATE.render(Context(context)))
Expand Down
28 changes: 18 additions & 10 deletions metro_map_saver/map_saver/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ def _station_count(self):
return 0

@staticmethod
def get_stations(data=None, data_version=2):
def get_stations(data=None, data_version=3):
stations = set()

if data_version == 2 and data:
if data_version >= 2 and data:
for x in data.get('stations', {}):
for y in data['stations'][x]:
stations.add(data['stations'][x][y].get('name', '').lower())
Expand Down Expand Up @@ -287,18 +287,26 @@ def generate_images(self):

points_by_color, stations, map_size = sort_points_by_color(mapdata, data_version=data_version)
shapes_by_color = {}
for color in points_by_color:
points_this_color = points_by_color[color]['xy']

lines, singletons = find_lines(points_this_color)
shapes_by_color[color] = {'lines': lines, 'points': singletons}

thumbnail_svg = get_svg_from_shapes_by_color(shapes_by_color, map_size, line_size, default_station_shape, points_by_color, stations)
if data_version <= 2:
for color in points_by_color:
points_this_color = points_by_color[color]['xy']

lines, singletons = find_lines(points_this_color)
shapes_by_color[color] = {'lines': lines, 'points': singletons}
elif data_version >= 3:
for color in points_by_color:
shapes_by_color[color] = {}
for width_style in points_by_color[color]:
points_this_color_width_style = points_by_color[color][width_style]
lines, singletons = find_lines(points_this_color_width_style)
shapes_by_color[color][width_style] = {'lines': lines, 'points': singletons}

thumbnail_svg = get_svg_from_shapes_by_color(shapes_by_color, map_size, line_size, default_station_shape, points_by_color, stations, data_version)

thumbnail_svg_file = ContentFile(thumbnail_svg, name=f"t{self.urlhash}.svg")
self.thumbnail_svg = thumbnail_svg_file

svg = add_stations_to_svg(thumbnail_svg, line_size, default_station_shape, points_by_color, stations)
svg = add_stations_to_svg(thumbnail_svg, line_size, default_station_shape, points_by_color, stations, data_version)
svg_file = ContentFile(svg, name=f"{self.urlhash}.svg")
self.svg = svg_file
self.save()
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions metro_map_saver/map_saver/static/assets/icons/mmm-icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions metro_map_saver/map_saver/static/assets/icons/ruler.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions metro_map_saver/map_saver/static/assets/icons/tags.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 32 additions & 3 deletions metro_map_saver/map_saver/static/css/metromapmaker.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ input[type=checkbox]{margin:4px 0 0;margin-top:1px\9;line-height:normal}
#controls:not(.collapsed) button:not(.width-100):not(.rail-line) {
width: 45%;
height: 6rem;
overflow: clip;
}

#controls:not(.collapsed) #toolbox > button:not(.rail-line) {
Expand Down Expand Up @@ -185,6 +186,10 @@ body {
font-family: Helvetica, Arial, sans-serif;
}

.hidden {
display: none;
}

#title, #autosave-indicator {
color: black;
font-size: 2rem;
Expand Down Expand Up @@ -248,7 +253,7 @@ div.mmm-help-setting {
left: 0;
}

canvas#grid-canvas, canvas#hover-canvas {
canvas#grid-canvas, canvas#hover-canvas, canvas#ruler-canvas {
z-index: 100;
width: 100%;
height: auto;
Expand Down Expand Up @@ -304,8 +309,31 @@ canvas#grid-canvas, canvas#hover-canvas {
border: 0.5rem solid transparent;
}

#controls.collapsed #line-color-options {
overflow-x: clip;
width: 100%;
}

#controls.collapsed #line-color-options fieldset {
border: 0;
padding: 0;
}

#controls.collapsed button.rail-line {
width: 6rem;
overflow-wrap: break-word;
width: 5.333rem;
}

#controls.collapsed .hide-when-collapsed {
display: none;
}

#controls:not(.collapsed) #ruler-icon-xy-container {
margin-top: 0.5rem;
}

#controls.collapsed #ruler-icon-xy-container svg {
display: none;
}

#tool-line-options button.rail-line:hover {
Expand Down Expand Up @@ -499,4 +527,5 @@ select {

#shareable-map-link {
word-break: break-word;
}
}

Loading

0 comments on commit 96e1ef2

Please sign in to comment.