Я сделал правку внизу, обнаружив новую проблему (почему нет Канады и ненадежность Shapely и Pyproj)
Несмотря на то, что это не совсем решение проблемы, я считаю, что такой подход имеет больший потенциал, чем использование pyproc и Shapely, и в будущем, если вы будете делать больше Ascii-графики, это даст вам больше возможностей и гибкости. Сначала напишу плюсы и минусы.
PS: Изначально я хотел найти проблему в вашем коде, но у меня были проблемы с его запуском, потому что pyproj возвращал мне какую-то ошибку.
ПЛЮСЫ
1) Я смог извлечь все точки (Канады действительно не хватает) и повернуть изображение
2) Обработка выполняется очень быстро, поэтому вы можете создавать анимированные рисунки Ascii.
3) Печать выполняется сразу без необходимости зацикливания
МИНУСЫ (известные проблемы, решаемые)
1) Это отношение определенно не правильно передает геокоординаты - слишком плоское, оно должно выглядеть более сферическим
2) Я не тратил время на поиск решения для заполнения границ, поэтому только границы имеют «*». Поэтому для этого отношения необходимо найти алгоритм заполнения стран. Я думаю, что это не должно быть проблемой, так как файл JSON содержит страны, разделенные
3) Вам нужны 2 дополнительные библиотеки помимо numpy - opencv (вместо этого вы можете использовать PIL) и Colorama, потому что мой пример анимирован, и мне нужно было «очистить» терминал, переместив курсор на (0,0) вместо использования os.system( 'клс')
4) Я заставил его работать только в python 3. В python 2 это тоже работает, но я получаю сообщение об ошибке с sys.stdout.buffer
Измените размер шрифта на терминале на самую низкую точку, чтобы напечатанные символы помещались в терминале. Меньше шрифт, лучше разрешение
Анимация должна выглядеть так, будто карта «вращается» ![введите здесь описание изображения](https://i. stack.imgur.com/UhXri.png)
Я использовал немного вашего кода для извлечения данных. Шаги в комментариях
import json
import sys
import numpy as np
import colorama
import sys
import time
import cv2
#understand terminal_size as how many letters in X axis and how many in Y axis. Sorry not good name
if len(sys.argv)>1:
terminal_size = (int(sys.argv[1]),int(sys.argv[2]))
else:
terminal_size=(230,175)
with open('world-countries.json') as f:
countries = []
minimal = 0 # This can be dangerous. Expecting negative values
maximal = 0 # Expecting bigger values than 0
for feature in json.load(f)['features']: # getting data - I pretend here, that geo coordinates are actually indexes of my numpy array
indexes = np.int16(np.array(feature['geometry']['coordinates'][0])*2)
if indexes.min()<minimal:
minimal = indexes.min()
if indexes.max()>maximal:
maximal = indexes.max()
countries.append(indexes)
countries = (np.array(countries)+np.abs(minimal)) # Transform geo-coordinates to image coordinates
correction = np.abs(minimal) # because geo-coordinates has negative values, I need to move it to 0 - xaxis
colorama.init()
def move_cursor(x,y):
print ("\x1b[{};{}H".format(y+1,x+1))
move = 0 # 'rotate' the globe
for i in range(1000):
image = np.zeros(shape=[maximal+correction+1,maximal+correction+1]) #creating clean image
move -=1 # you need to rotate with negative values
# because negative one are by numpy understood. Positive one will end up with error
for i in countries: # VERY STRANGE,because parsing the json, some countries has different JSON structure
if len(i.shape)==2:
image[i[:,1],i[:,0]+move]=255 # indexes that once were geocoordinates now serves to position the countries in the image
if len(i.shape)==3:
image[i[0][:,1],i[0][:,0]+move]=255
cut = np.where(image==255) # Bounding box
if move == -1: # creating here bounding box - removing empty edges - from sides and top and bottom - we need space. This needs to be done only once
max_x,min_x = cut[0].max(),cut[0].min()
max_y,min_y = cut[1].max(),cut[1].min()
new_image = image[min_x:max_x,min_y:max_y] # the bounding box
new_image= new_image[::-1] # reverse, because map is upside down
new_image = cv2.resize(new_image,terminal_size) # resize so it fits inside terminal
ascii = np.chararray(shape = new_image.shape).astype('|S4') #create container for asci image
ascii[:,:]='' #chararray contains some random letters - dunno why... cleaning it
ascii[:,-1]='\n' #because I pring everything all at once, I am creating new lines at the end of the image
new_image[:,-1]=0 # at the end of the image can be country borders which would overwrite '\n' created one step above
ascii[np.where(new_image>0)]='*' # transforming image array to chararray. Better to say, anything that has pixel value higher than 0 will be star in chararray mask
move_cursor(0,0) # 'cleaning' the terminal for new animation
sys.stdout.buffer.write(ascii) # print into terminal
time.sleep(0.025) # FPS
Возможно, было бы неплохо объяснить, что является основным алгоритмом в коде. Мне нравится использовать numpy везде, где я могу. Все дело в том, что я делаю вид, что координаты на изображении, или что бы там ни было (в вашем случае геокоординаты) — это индексы матриц. У меня есть 2 матрицы - Real Image и Charray as Mask. Затем я беру индексы интересных пикселей в реальном изображении и для тех же индексов в Charray Mask присваиваю любую букву, которую хочу. Благодаря этому всему алгоритму не требуется ни одного цикла.
О будущих возможностях
Представьте, что у вас также будет информация о местности (высоте). Допустим, вы каким-то образом создаете изображение карты мира в градациях серого, где оттенки серого выражают высоту. Такое изображение в градациях серого будет иметь форму x, y. Вы подготовите 3Dmatrix с формой = [x,y,256]. Для каждого слоя из 256 в 3D-матрице вы назначаете одну букву «….;;;;### и так далее», которая будет обозначать оттенок. Когда вы это подготовите, вы можете взять изображение в градациях серого, где любой пиксель фактически будет иметь 3 координаты: x, y и значение оттенка. Таким образом, у вас будет 3 массива индексов из вашего изображения карты grascale -> x,y,shade. Ваш новый charray будет просто извлечен из вашей 3Dmatrix с буквами слоев, потому что:
#Preparation phase
x,y = grayscale.shape
3Dmatrix = np.chararray(shape = [x,y,256])
table = ' ......;;;;;;;###### ...'
for i in range(256):
3Dmatrix[:,:,i] = table[i]
x_indexes = np.arange(x*y)
y_indexes = np.arange(x*y)
chararray_image = np.chararray(shape=[x,y])
# Ready to print
...
shades = grayscale.reshape(x*y)
chararray_image[:,:] = 3Dmatrix[(x_indexes ,y_indexes ,shades)].reshape(x,y)
Поскольку в этом процессе нет цикла, и вы можете сразу распечатать chararray, вы можете распечатать фильм в терминале с огромным FPS.
Например, если у вас есть кадры вращающейся земли, вы можете сделать что-то вроде этого — (250*70 букв), время рендеринга 0,03658 с.
![введите здесь описание изображения](https://i.stack.imgur.com/9EHdt.png)
Можно, конечно, довести это до крайности и сделать супер-разрешение в своем терминале, но в результате FPS будет не очень хорошим: 0,23157 с, то есть примерно 4-5 FPS. Интересно отметить, что такое отношение FPS огромно, но терминал просто не может справиться с печатью, поэтому такой низкий FPS связан с ограничениями терминала, а не расчетов, поскольку расчет такого высокого разрешения занял 0,00693 с, то есть 144 FPS. .
![введите здесь описание изображения](https://i.stack.imgur.com/C1IMe.png)
БОЛЬШОЕ ИЗМЕНЕНИЕ — противоречащие некоторым из приведенных выше утверждений
Я случайно открыл необработанный файл json и обнаружил, что есть КАНАДА и РОССИЯ с полными правильными координатами. Я сделал ошибку, полагаясь на тот факт, что у нас обоих не было канады в результате, поэтому я ожидал, что мой код в порядке. Внутри JSON данные имеют другую НЕУНИФИЦИРОВАННУЮ структуру. В России и Канаде есть «Мультиполигон», поэтому вам нужно перебрать его.
Что это значит? Не полагайтесь на Shapely и pyproj. Очевидно, что они не могут извлечь некоторые страны, и если они не могут сделать это надежно, вы не можете ожидать, что они сделают что-то более сложное.
После изменения кода все в порядке
КОД: Вот как правильно загрузить файл
...
with open('world-countries.json') as f:
countries = []
minimal = 0
maximal = 0
for feature in json.load(f)['features']: # getting data - I pretend here, that geo coordinates are actually indexes of my numpy array
for k in range((len(feature['geometry']['coordinates']))):
indexes = np.int64(np.array(feature['geometry']['coordinates'][k]))
if indexes.min()<minimal:
minimal = indexes.min()
if indexes.max()>maximal:
maximal = indexes.max()
countries.append(indexes)
...
![введите здесь описание изображения](https://i.stack.imgur.com/BpH5K.png)
person
Martin
schedule
29.03.2019