Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 283afa0

Browse files
committed
Add new script - hoi4statemapgenerator.py
Given valid provinces.bmp, definition.csv files and a folder of state history files, generate an image containing a map of states with their IDs.
1 parent def438d commit 283afa0

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

‎python3/hoi4statemapgenerator.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
#!/usr/bin/python3
2+
import argparse
3+
import os
4+
import sys
5+
import re
6+
from collections import defaultdict
7+
import random
8+
import pickle
9+
10+
try:
11+
from PIL import Image, ImageDraw, ImageFont
12+
except:
13+
sys.exit("Requires pillow pip package to be installed. Run:\npip install pillow\nor\npip3.6 install pillow\ndepending on your installation and start the script again.\nMore info on installing packages: https://docs.python.org/3/installing/index.html")
14+
15+
try:
16+
import numpy as np
17+
except:
18+
sys.exit("Requires numpy pip package to be installed. Run:\npip install numpy\nor\npip3.6 install numpy\ndepending on your installation and start the script again.\nMore info on installing packages: https://docs.python.org/3/installing/index.html")
19+
20+
#############################
21+
###
22+
### HoI 4 State IDs Map Generator by Yard1, originally for Equestria at War mod
23+
### Written in Python 3.6
24+
### Requires pillow and numpy pip packages to be installed. More info on installing packages: https://docs.python.org/3/installing/index.html
25+
###
26+
### Copyright (c) 2018 Antoni Baum (Yard1)
27+
### Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
28+
### The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
29+
### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30+
###
31+
### usage: hoi4statemapgenerator.py [-h] [-c COLORS] [-nid]
32+
### provinces definition states output
33+
###
34+
### Given valid provinces.bmp, definition.csv files and a folder of state history
35+
### files, generate an image containing a map of states with their IDs.
36+
###
37+
### positional arguments:
38+
### provinces Path to provinces.bmp file
39+
### definition Path to definition.csv file
40+
### states Path to history/states folder
41+
### output Name of output file
42+
###
43+
### optional arguments:
44+
### -h, --help show this help message and exit
45+
### -c COLORS, --colors COLORS
46+
### Name of pregenerated colors.pickle file (Default:
47+
### hoi4statemapgenerator_colors.pickle)
48+
### -nid, --no_ids Do not put IDs on the map (Default: False)
49+
###
50+
#############################
51+
52+
BLUE_RBG = (68, 107, 163)
53+
54+
def readable_dir(prospective_dir):
55+
if not os.path.isdir(prospective_dir):
56+
raise Exception("readable_dir:{0} is not a valid path".format(prospective_dir))
57+
if os.access(prospective_dir, os.R_OK):
58+
return prospective_dir
59+
else:
60+
raise Exception("readable_dir:{0} is not a readable dir".format(prospective_dir))
61+
62+
#############################
63+
64+
# Copyright 2011 Álvaro Justen [alvarojusten at gmail dot com]
65+
# License: GPL <http://www.gnu.org/copyleft/gpl.html>
66+
# Begin copyright
67+
68+
def get_random_color(pastel_factor = 0.5):
69+
return [(x+pastel_factor)/(1.0+pastel_factor) for x in [random.uniform(0,1.0) for i in [1,2,3]]]
70+
71+
def color_distance(c1,c2):
72+
return sum([abs(x[0]-x[1]) for x in zip(c1,c2)])
73+
74+
def generate_new_color(existing_colors,pastel_factor = 0.5):
75+
max_distance = None
76+
best_color = None
77+
for _ in range(0,100):
78+
color = get_random_color(pastel_factor = pastel_factor)
79+
if not existing_colors:
80+
return color
81+
best_distance = min([color_distance(color,c) for c in existing_colors])
82+
if not max_distance or best_distance > max_distance:
83+
max_distance = best_distance
84+
best_color = color
85+
return best_color
86+
87+
# End copyright
88+
89+
#############################
90+
91+
def load_provinces(name):
92+
print("Reading file " + name + "...")
93+
im = Image.open(name)
94+
return im
95+
96+
def load_definition(name):
97+
print("Reading file " + name + "...")
98+
with open(name, "r") as f:
99+
lines = f.read().splitlines()
100+
provinces = {}
101+
for line in lines:
102+
line = line.split(";")
103+
provinces[int(line[0])] = (int(line[1]), int(line[2]), int(line[3]))
104+
return provinces
105+
106+
def load_state_file(name, states_dict):
107+
print("Reading file " + name + "...")
108+
with open(name, "r") as f:
109+
file_str = f.read()
110+
state_id = int(re.search(r"(?:id\s*=\s*)([0-9]+)", file_str).group(1))
111+
province_ids = [int(x) for x in re.search(r"(?:provinces\s*=\s*\{)((\s|.)*?)(?:\})", file_str).group(1).split()]
112+
states_dict[state_id] = province_ids
113+
114+
def create_states_map(colors_replacement_dict, provinces_image, water_color):
115+
water_color = (water_color[0], water_color[1], water_color[2])
116+
pixels = provinces_image.load()
117+
for i in range(provinces_image.size[0]):
118+
for j in range(provinces_image.size[1]):
119+
if pixels[i, j] in colors_replacement_dict:
120+
pixels[i, j] = colors_replacement_dict[pixels[i, j]][0]
121+
else:
122+
pixels[i, j] = water_color
123+
124+
def create_states_map_with_id(colors_replacement_dict, provinces_image, water_color):
125+
state_pixels = defaultdict(list)
126+
water_color = (water_color[0], water_color[1], water_color[2])
127+
pixels = provinces_image.load()
128+
for i in range(provinces_image.size[0]):
129+
for j in range(provinces_image.size[1]):
130+
if pixels[i, j] in colors_replacement_dict:
131+
res = colors_replacement_dict[pixels[i, j]]
132+
pixels[i, j] = res[0]
133+
state_pixels[res[1]].append((i, j))
134+
else:
135+
pixels[i, j] = water_color
136+
137+
# thanks to Martin Stancsics, https://stackoverflow.com/questions/37519238/python-find-center-of-object-in-an-image
138+
m = None
139+
(X, Y) = provinces_image.size
140+
draw = ImageDraw.Draw(provinces_image)
141+
font = ImageFont.truetype("ARIALN.TTF", 10)
142+
for state, pixels in state_pixels.items():
143+
m = np.zeros((X, Y))
144+
for pixel in pixels:
145+
m[pixel] = 1
146+
m = m / np.sum(np.sum(m))
147+
148+
dx = np.sum(m, 1)
149+
dy = np.sum(m, 0)
150+
151+
cx = np.sum(dx * np.arange(X))
152+
cy = np.sum(dy * np.arange(Y))
153+
154+
w, h = font.getsize(str(state))
155+
draw.text((cx-w/2,cy-h/2), str(state), fill="black", font=font)
156+
157+
def get_colors(name):
158+
try:
159+
print("Reading file " + name + "...")
160+
with open(name, "rb") as handle:
161+
colors = pickle.load(handle)
162+
except:
163+
print("No file " + name + "found, creating new colors...")
164+
colors = [[(1/255)*BLUE_RBG[0], (1/255)*BLUE_RBG[1], (1/255)*BLUE_RBG[2]]]
165+
if len(states_dict) > len(colors)-1:
166+
for _ in range(len(states_dict)-((len(colors)-1))):
167+
colors.append(generate_new_color(colors))
168+
try:
169+
colors.remove([[(1/255)*BLUE_RBG[0], (1/255)*BLUE_RBG[1], (1/255)*BLUE_RBG[2]]])
170+
except:
171+
pass
172+
print("Saving file " + name + "...")
173+
with open(name, "rb") as handle:
174+
pickle.dump(colors, handle, protocol=pickle.HIGHEST_PROTOCOL)
175+
176+
colors.insert(0, [(1/255)*BLUE_RBG[0], (1/255)*BLUE_RBG[1], (1/255)*BLUE_RBG[2]])
177+
return colors
178+
179+
#############################
180+
181+
parser = argparse.ArgumentParser(description='Given valid provinces.bmp, definition.csv files and a folder of state history files, generate an image containing a map of states with their IDs.')
182+
parser.add_argument('provinces',
183+
help='Path to provinces.bmp file')
184+
parser.add_argument( 'definition',
185+
help='Path to definition.csv file')
186+
parser.add_argument( 'states',
187+
help='Path to history/states folder')
188+
parser.add_argument( 'output',
189+
help='Name of output file')
190+
parser.add_argument('-c', '--colors', required=False, default="hoi4statemapgenerator_colors.pickle",
191+
help='Name of pregenerated colors.pickle file (Default: hoi4statemapgenerator_colors.pickle)')
192+
parser.add_argument( '-nid', '--no_ids', action='store_true', required=False, default=False,
193+
help='Do not put IDs on the map (Default: False)')
194+
195+
args = parser.parse_args()
196+
197+
try:
198+
dir = readable_dir(args.states)
199+
except:
200+
sys.exit("states is not a vaild folder.")
201+
202+
states_path = args.states
203+
states_dict = {}
204+
for file in os.listdir(states_path):
205+
if file.endswith(".txt"):
206+
load_state_file(os.path.join(states_path, file), states_dict)
207+
208+
colors = get_colors(args.colors)
209+
210+
provinces = load_definition(args.definition)
211+
212+
province_map = load_provinces(args.provinces)
213+
214+
colors_replacement_dict = {}
215+
216+
for state_id, state_provinces in states_dict.items():
217+
color = [round(255 * x) for x in colors.pop()]
218+
219+
#print("STATE %s: COLOR: %s" % (str(state_id), color))
220+
for province in state_provinces:
221+
colors_replacement_dict[provinces[province]] = ((color[0], color[1], color[2]), state_id)
222+
223+
print("Generating map image - this may take a while...")
224+
if args.no_ids:
225+
create_states_map(colors_replacement_dict, province_map, [round(255 * x) for x in colors[0]])
226+
else:
227+
create_states_map_with_id(colors_replacement_dict, province_map, [round(255 * x) for x in colors[0]])
228+
229+
province_map.show()
230+
print("Saving file " + args.output + "...")
231+
province_map.save(args.output, "PNG")
31 KB
Binary file not shown.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /