Как обрабатывать ошибку pyproj datadir / CRS при создании исполняемого файла с помощью pyinstaller

Программа, которую я создаю, предназначена для создания нескольких точек в пределах нескольких почтовых индексов и определения расстояния от каждой из этих точек до каждой точки в каждом почтовом индексе, которая находится в пределах 5 миль от интересующего почтового индекса. Это делается с помощью службы матрицы расстояний Google Maps и сбора данных о расстоянии. Я создал длинную функцию, которая выполняет несколько функций (я думаю, что ее нужно еще немного разбить). Вот где, я думаю, проблема.

Все работает так, как должно, однако, когда я перехожу к созданию исполняемого файла с помощью Pyinstaller, я получаю несколько ошибок при его первой загрузке и при попытке запустить основную функцию моей программы. Ошибки, похоже, связаны с pyproj и geopandas.

Я видел эту проблему еще в нескольких местах. Мне не удалось успешно применить решения, обсуждаемые в тех местах. Представленные решения включали:

  • понизить версию pyproj до 1.9.6 - см. ошибку ниже

  • добавить файл перехвата в pyinstaller - файл перехвата уже был в каталоге

  • включить pyproj._datadir и pyproj.datadir в раздел скрытого импорта созданного файла спецификации.

  • используйте os.environ ['PROJ_LIB'] и установите его в общую папку, находящуюся по адресу "/ share

Пакеты, которые я использую:

import pandas as pd
import tkinter as tk
import tkinter.filedialog
import os
import geopandas as gpd
from shapely.geometry import Point,LineString
import shapely.wkt
import googlemaps
from googlemaps.exceptions import ApiError
import datetime
from statistics import median
import _thread

Файл спецификации:


# -*- mode: python -*-

block_cipher = None


a = Analysis(['main.py'],
             pathex=['C:\\Users\\Keagan\\PycharmProjects\\upwork_jobs\\pet_sitting2\\gui'],
             binaries=[],
             datas=[],
             hiddenimports=['fiona._shim','fiona.schema','pyproj._datadir','pyproj.datadir'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='main',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='main')

Основная функция:

    def model_distances(self, reference_names_list, reference_zips_df,zips_and_points_gdf,api_key):

        gmaps = googlemaps.Client(api_key)

        def find_key_value_connection(poi, to_location, list_of_dicts):

            for item in list_of_dicts:
                if poi == item["poi"] and to_location == item["to_location"] or to_location == item[
                    "poi"] and poi == \
                        item["to_location"]:
                    return True

            return False

        def projection(origin_projection, to_projection, geometry_object):
            project = partial(
                pyproj.transform,
                pyproj.Proj(init=origin_projection),
                pyproj.Proj(init=to_projection)
            )

            return transform(project, geometry_object)

        zip_code_intersect_list = []

        completed_locations_dict = {}
        completed_locations_list = []

        count = 0
        google_credit_count = 0
        completed_locations_df = None

        buffer_list = []



        for name in reference_names_list:
            print("we are on: {}".format(name))

            if os.path.isfile("output_files/completed_locations_{}.xlsx".format(name)) and completed_locations_df is None:
                print("found backup, opening it")

                completed_locations_df = pd.read_excel("output_files/completed_locations_{}.xlsx".format(name))

                for item in completed_locations_df.itertuples():
                    completed_locations_dict["poi"] = int(item.poi)
                    completed_locations_dict["to_location"] = int(item.to_location)
                    completed_locations_dict["poi_zip"] = item.poi_zip
                    completed_locations_dict["to_zip"] = item.to_zip
                    completed_locations_dict["poi_name"] = item.poi_name
                    completed_locations_dict["to_name"] = item.to_name
                    completed_locations_dict["id"] = item.id
                    completed_locations_dict["distance"] = float(item.distance)
                    completed_locations_dict["time"] = float(item.time)

                    completed_locations_list.append(completed_locations_dict.copy())

            elif not os.path.isfile("output_files/completed_locations_{}.xlsx".format(name)):
                print("creating a backup")
                completed_locations_df = pd.DataFrame()
                completed_locations_df.to_excel("completed_locations_{}.xlsx".format(name))

            for zip in reference_zips_df.itertuples():

                if zip.name == name:
                    print("we are in zipcode: {}".format(zip.zip))
                    for poi in zips_and_points_gdf.itertuples():
                        if str(poi.zip_left) == str(zip.zip):
                            buffer = ""
                            poi_zip = ""
                            if poi_zip == None or poi.zip_left != poi_zip:
                                poi_zip = poi.zip_left
                                buffer = shapely.wkt.loads(poi.zip_center_point).buffer(8046)
                                buffer_list.append(buffer)

                            for to_location in zips_and_points_gdf.itertuples():
                                if poi.zip_left != to_location.zip_left and to_location.geometry.intersects(
                                        buffer) and to_location.zip_left not in zip_code_intersect_list:
                                    zip_code_intersect_list.append(to_location.zip_left)

                            for to_location in zips_and_points_gdf.itertuples():
                                if to_location.zip_left in zip_code_intersect_list and to_location.name_left == name:

                                    if find_key_value_connection(int(poi.Index), int(to_location.Index),
                                                                 completed_locations_list):

                                        print(
                                            "point at index {} was already calculated to point at index {}, google credit at: {}".format(
                                                poi.Index, to_location.Index, google_credit_count))
                                    else:
                                        google_credit_count += 1
                                        count += 1

                                        print(
                                            "calculating point at index {} to index {}, google credit at: {}".format(
                                                poi.Index, to_location.Index, google_credit_count))

                                        new_poi = projection("epsg:26910", "epsg:4326", poi.geometry)
                                        new_to_location = projection("epsg:26910", "epsg:4326", to_location.geometry)


                                        result = gmaps.distance_matrix((new_poi.y, new_poi.x),
                                                                       (new_to_location.y,new_to_location.x))

                                        completed_locations_dict["poi"] = int(poi.Index)
                                        completed_locations_dict["to_location"] = int(to_location.Index)
                                        completed_locations_dict["poi_zip"] = poi.zip_left
                                        completed_locations_dict["to_zip"] = to_location.zip_left
                                        completed_locations_dict["poi_name"] = zip.name
                                        completed_locations_dict["to_name"] = to_location.name_left
                                        completed_locations_dict["id"] = str(poi.zip_left) + str(
                                            poi.Index) + "-" + str(to_location.zip_left) + str(to_location.Index)

                                        try:
                                            completed_locations_dict["time"] = \
                                            result["rows"][0]["elements"][0]["duration"]["value"] / 60
                                            completed_locations_dict["distance"] = \
                                            result["rows"][0]["elements"][0]["distance"]["value"] / 1609.3
                                        except KeyError:
                                            completed_locations_dict["time"] = "nan"
                                            completed_locations_dict["distance"] = "nan"

                                        completed_locations_list.append(completed_locations_dict.copy())



                                        if count > 500:
                                            print("backup exists appending new df to backup")


                                            completed_locations_df = pd.DataFrame(completed_locations_list)
                                            completed_locations_df.to_excel("output_files/completed_locations_{}.xlsx".format(name))

                                            count = 0

                                        if google_credit_count >= 10000:
                                            continue_program = input(
                                                "desired google credit has hit $50, continue or change keys?(continue/change/quit): ")
                                            while continue_program != "continue":
                                                if continue_program == "quit":
                                                    # with open("backup_save.json", "w") as backup_file:
                                                    #     json.dump(completed_locations_list.copy(), backup_file)
                                                    completed_locations_df = pd.DataFrame(completed_locations_list)
                                                    completed_locations_df.to_excel("output_files/completed_locations_{}.xlsx".format(name))

                                                    print("saving to excel")
                                                    quit()
                                                new_key = input("please insert a new key: ")
                                                gmaps = googlemaps.Client(key=new_key)
                                                try:
                                                    # res = gmaps.geocode("Austin, Texas")
                                                    continue_program = input("valid key, continue? (continue/quit): ")
                                                except (ValueError, ApiError):
                                                    new_key = input("invalid key, try again: ")

                                            google_credit_count = 0

                        zip_code_intersect_list = []

        completed_locations_df = pd.DataFrame(completed_locations_list)


        return completed_locations_df

При попытке понизить версию pyproj до 1.9.6:

UnsatisfiableError: The following specifications were found to be incompatible with each other:

При первом запуске инструмента:

Warning:
The MATPLOTLIBDATA environment variable was deprecated in Matplotlib 3.1 and will be removed in 3.3.
  exec(bytecode, module.__dict__)
Traceback (most recent call last):
  File "site-packages\pyproj\datadir.py", line 101, in get_data_dir
pyproj.exceptions.DataDirError: Valid PROJ data directory not found.Either set the path using the environmental variable PROJ_LIB or with `pyproj.datadir.set_data_dir`.
Exception ignored in: 'pyproj._datadir.get_pyproj_context'
Traceback (most recent call last):
  File "site-packages\pyproj\datadir.py", line 101, in get_data_dir
pyproj.exceptions.DataDirError: Valid PROJ data directory not found.Either set the path using the environmental variable PROJ_LIB or with `pyproj.datadir.set_data_dir`.
proj_create: Cannot find proj.db
proj_create: init=epsg:/init=IGNF: syntax not supported in non-PROJ4 emulation mode
Invalid projection: +init=epsg:4326 +type=crs

При запуске инструмента:

<code that runs fine before>


Traceback (most recent call last):
  File "site-packages\pyproj\datadir.py", line 101, in get_data_dir
pyproj.exceptions.DataDirError: Valid PROJ data directory not found.Either set the path using the environmental variable PROJ_LIB or with `pyproj.datadir.set_data_dir`.
Exception ignored in: 'pyproj._datadir.get_pyproj_context'
Traceback (most recent call last):
  File "site-packages\pyproj\datadir.py", line 101, in get_data_dir
pyproj.exceptions.DataDirError: Valid PROJ data directory not found.Either set the path using the environmental variable PROJ_LIB or with `pyproj.datadir.set_data_dir`.
proj_create: Cannot find proj.db
proj_create: init=epsg:/init=IGNF: syntax not supported in non-PROJ4 emulation mode
Unhandled exception in thread started by <bound method ZipAnalysisGUI.analyze_data of <__main__.ZipAnalysisGUI object at 0x000001DAAD51A668>>
Traceback (most recent call last):
  File "main.py", line 480, in analyze_data
  File "main.py", line 237, in model_distances
  File "main.py", line 157, in projection
  File "site-packages\pyproj\proj.py", line 147, in __init__
  File "site-packages\pyproj\crs.py", line 391, in from_user_input
  File "site-packages\pyproj\crs.py", line 260, in __init__
  File "pyproj/_crs.pyx", line 1292, in pyproj._crs._CRS.__init__
pyproj.exceptions.CRSError: Invalid projection: +init=epsg:26910 +type=crs

Я думаю, что это происходит из-за ошибки выше:

        def projection(origin_projection, to_projection, geometry_object):
            project = partial(
                pyproj.transform,
                pyproj.Proj(init=origin_projection),
                pyproj.Proj(init=to_projection)
            )

            return transform(project, geometry_object)

Опять же, все работает хорошо при запуске из pycharm. Как только я пытаюсь запустить его как исполняемый файл, он начинает разваливаться. Я почти уверен, что это неправильно с вышеуказанной функцией, но я не могу определить почему. При необходимости я могу поделиться дополнительным кодом или всем файлом.


person Keagan McNew    schedule 26.08.2019    source источник
comment
Наверное. У меня такая же проблема с приложением, работающим с использованием miniconda в качестве python и вызывающим bash, который активирует коды из crontab. Как только я запускаю тот же код, вызывая таким же образом bash /path/to/my/bas.sh, он отлично работает.   -  person Isaque Daniel    schedule 02.09.2019
comment
Можете ли вы попробовать pyproj==2.4.0 и посмотреть, сохраняется ли проблема?   -  person snowman2    schedule 25.09.2019


Ответы (1)