Skip to content

Instantly share code, notes, and snippets.

@mikkohei13
Last active February 10, 2024 16:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikkohei13/9a7132e29e352a32645a768174f31d13 to your computer and use it in GitHub Desktop.
Save mikkohei13/9a7132e29e352a32645a768174f31d13 to your computer and use it in GitHub Desktop.
Reads EPSG:2393 (KKJ / Finland Uniform Coordinate System) coordinates with optional color and draws them as dots on a SVG map.
# Reads EPSG:2393 (KKJ / Finland Uniform Coordinate System) coordinates with optional color and draws them as dots on a SVG map.
# Depends on:
# GeoJSON data from https://gist.github.com/mikkohei13/0acf09633a1635b5363d47c5603fd789
# Sample data from https://gist.github.com/mikkohei13/d03316b75393a1718be5232986e0e2eb
import re
import json
def extract_polygons_from_geojson(file_path):
with open(file_path, 'r') as file:
data = json.load(file)
polygons = []
for feature in data['features']:
geometry = feature['geometry']
coordinates = geometry['coordinates']
if geometry['type'] == 'Polygon':
# For Polygon, take the first set of coordinates (outer boundary)
polygon = [(lat, lon) for lon, lat in coordinates[0]]
polygons.append(polygon)
return polygons
def is_valid_hex_color(color):
"""
Validates if the input string is a valid hexadecimal color value.
Allows both 3 and 6 digit color values.
:param color: str, the hexadecimal color value to validate.
:return: bool, True if the color is valid, False otherwise.
"""
# Regular expression to match 3 or 6 digit hexadecimal colors
pattern = r'^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$'
# Use the re.match() function to check if the pattern matches the input color
return bool(re.match(pattern, color))
# Function to read tsv file of coordinates
def read_tsv(filename):
"""
Reads a tab separated file with two columns of coordinates, latitude (N) and longitude (E), and optionally hexadecimal color code.
Returns a SVG map file.
:param filename: str, filename.
:return: list, a list of coordinate and optionally color tuples.
"""
data = []
with open(filename) as file:
for line in file:
results = line.strip().split('\t')
if len(results) < 2:
print("Invalid line, skipping.")
continue
if len(results) > 3:
print("Invalid line, skipping.")
continue
if len(results) == 2:
y, x = results
color = "#000000"
print("Color not defined, using black instead.")
else:
y, x, color = results
# Validate the color
if not is_valid_hex_color(color):
color = "#000000"
print("Invalid color code, using black instead.")
data.append((int(y), int(x), color))
return data
def generate_svg(coordinates, polygons = None, filename = "map.svg", line_color = "#CCCCCC", line_width = 1):
# Define the bounding box of the coordinates in EPSG:2393
min_y = 6610000 / 1000
max_y = 7780000 / 1000
min_x = 3060000 / 1000
max_x = 3740000 / 1000
height = max_y - min_y
width = max_x - min_x
# Start the SVG file content
svg_content = [f"<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='{ width }' height='{ height }'>"]
# Polygons
for polygon in polygons:
points = []
for coord in polygon:
# Unpack the coordinate tuple
y, x = coord
# Normalize the coordinates to fit within the SVG dimensions
y_normalized = int(y / 1000 - min_y)
x_normalized = int(x / 1000 - min_x)
# Flip the y-axis to match the SVG coordinate system
y_normalized = int(height - y_normalized)
# Add this point to the list of points for the polygon
points.append(f"{ x_normalized },{ y_normalized }")
# Join the points into a string for the polygon points attribute
points_str = " ".join(points)
# Add the polygon to the SVG
svg_content.append(f"<polygon points='{ points_str }' style='fill:none;stroke:{ line_color };stroke-width:{ line_width }' />\n")
# Points
for point_coord in coordinates:
# Unpack
y, x, color = point_coord
y = y / 1000
x = x / 1000
y_normalized = y - min_y
x_normalized = x - min_x
# Flip the y-axis to match the SVG coordinate system
y_normalized = height - y_normalized
# Add a circle for each coordinate
svg_content.append(f"<circle cx='{ x_normalized }' cy='{ y_normalized }' r='5' fill='{ color }' />")
# Close the SVG tag
svg_content.append('</svg>')
# Write the SVG content to a file
with open(filename, 'w') as file:
file.write('\n'.join(svg_content))
# Get the GeoJSON data from https://gist.github.com/mikkohei13/0acf09633a1635b5363d47c5603fd789
geojson_filename = "fin_ykj.geojson"
polygons = extract_polygons_from_geojson(geojson_filename)
# Get sample data from https://gist.github.com/mikkohei13/d03316b75393a1718be5232986e0e2eb
filename = "data/atlassquares.tsv"
filename = "data/sample.tsv"
filename = "data/atlas-colors.tsv"
atlas_squares = read_tsv(filename)
generate_svg(atlas_squares, polygons, "newmap.svg", "#FF0000", 2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment