Я сделал правку внизу, обнаружив новую проблему (почему нет Канады и ненадежность Shapely и Pyproj)
Хотя это не совсем решает проблему,Я полагаю, что такое отношение имеет больший потенциал, чем использование pyproc и Shapely, и в будущем, если вы будете больше заниматься искусством Ascii, у вас будет больше возможностей и гибкости.Сначала я напишу плюсы и минусы.
PS: Изначально я хотел найти проблему в вашем коде, но у меня были проблемы с его запуском, потому что pyproj возвращал мне какую-то ошибку.
PROS
1) Мне удалось извлечь все точки (Канада действительно отсутствует) и повернуть изображение
2) Обработка очень быстрая, и поэтому вы можете создать Анимированный Ascii art .
3) Печать выполняется сразу без необходимости зацикливания
CONS (известные проблемы, решаемые)
1) Это отношение определенно неправильно переводит гео-координаты - слишком плоское, оно должно выглядеть более сферическим
2) Мне не потребовалось время, чтобы попытаться найти решение для заполнения границ, поэтому только границы имеют '*».Поэтому это отношение должно найти алгоритм для заполнения стран.Я думаю, что это не должно быть проблемой, так как файл JSON содержит страны, разделенные
3) Вам нужно 2 дополнительные библиотеки помимо numpy - opencv (вместо этого вы можете использовать PIL) и Colorama, потому что мой пример анимирован, и мне нужно было 'очистите терминал, переместив курсор на (0,0) вместо использования os.system ('cls')
4) Я заставил его работать только в python 3 .В python 2 это тоже работает, но я получаю ошибку с sys.stdout.buffer
Измените размер шрифта на терминале на самую низкую точку , чтобы напечатанные символы помещались в терминале.Меньший шрифт, лучшее разрешение
Анимация должна выглядеть так, как будто карта «вращается»
Я использовал немного вашего кода для извлечения данных.Шаги в комментариях
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 я назначаю любую букву, которую я хочу.Благодаря этому весь алгоритм не нуждается в одном цикле.
О будущих возможностях
Представьте, что у вас также будет информация о местности (высоте).Допустим, вы каким-то образом создаете серое изображение карты мира, где серые оттенки выражают высоту.Такое изображение в градациях серого будет иметь форму x, y.Вы подготовите 3Dmatrix с формой = [x, y, 256].Для каждого слоя из 256 в трехмерной матрице вы назначаете одну букву «.... ;;;; ### и т. Д.», Которая будет выражать тень.Когда вы это подготовите, вы можете взять свое изображение в градациях серого, где любой пиксель будет фактически иметь 3 координаты: x, y и значение оттенка.Таким образом, у вас будет 3 массива индексов из вашего изображения карты масштаба -> x, y, тень.Ваш новый 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 с
Вы, конечно, можете довести это до крайности и сделать суперразрешение в своем терминале, но результирующий FPS не так хорош: 0,23157 с, то есть примерно 4-5 FPS. Интересно отметить, что это отношение FPS является огромным, но терминал просто не может обрабатывать печать, поэтому этот низкий FPS обусловлен ограничениями терминала, а не расчетами, так как вычисление этого высокого разрешения заняло 0,00693 с, то есть 144 FPS .
БОЛЬШОЕ РЕДАКТИРОВАНИЕ - противоречащее некоторым из приведенных выше утверждений
Я случайно открыл raw json файл и выяснил, что там КАНАДА и РОССИЯ с полными правильными координатами. Я сделал ошибку, полагаясь на тот факт, что у нас обоих не было Канады в результате, поэтому я ожидал, что мой код в порядке . Внутри JSON данные имеют различную НЕ-УНИФИЦИРОВАННУЮ структуру. В России и Канаде есть «Мультиполигон», поэтому вам нужно повторить его.
Что это значит? Не полагайтесь на Shapely и pyproj. Очевидно, что они не могут извлечь некоторые страны, и если они не могут сделать это надежно, вы не можете ожидать, что они сделают что-нибудь более сложное.
После изменения кода все в порядке
CODE: как правильно загрузить файл
...
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)
...