Last active
February 10, 2024 16:47
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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