Search code examples
pythonpyinstallergeospatialsetuptoolsgeopandas

cx_Freeze with GeoPandas geos_c.dll


I have a problem when I try to export my python file. Normally I used PyInstaller from a conda environment, but I read that cx_Freeze was easier to use in combination with geopandas.

I generated a test script:

# test.py
import numpy as np
import geopandas as gpd
import sys

from pyproj import _datadir, datadir

print("Test script:")
print(f"Executable {sys.executable}")
print("Read numpy list")
x = np.linspace(0, 10, 11)
print(x)

This script runs fine, I am using a conda environment. The line with from pyproj import _datadir, datadir is to prevent an import error of GeoPandas.

This is my setup script:

# setup.py
import cx_Freeze

cx_Freeze.setup(
        name = "test",
        version = "0.1",
        description = "test import geopandas",
        executables = [cx_Freeze.Executable("test.py")]
        )

I also tried another setup script:

import cx_Freeze
import sys
import os 

PYTHON_INSTALL_DIR = os.path.dirname(sys.executable)
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')

include_files = [(os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'), os.path.join('lib', 'tk86t.dll')),
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'), os.path.join('lib', 'tcl86t.dll'))]

packages = ["numpy", "tkinter",
            "geopandas", "shapely", "fiona"]

base = None
if sys.platform == "win32":
    base = "Win32GUI"

executables = [cx_Freeze.Executable("test.py", base=base)]

cx_Freeze.setup(
        name = "Network_Analyst",
        options = {"build_exe": {"packages":packages,
                                 "include_files":include_files}},
        version = "0.1",
        description = "Network analyst",
        executables = executables
        )

But I got the same error.

I have also tried this answer in combination with PyInstaller: Cannot import Geopandas with PyInstaller executable - despite running fine in the virtual env

But that did not work.

What I also don't understand is that the error is:

FileNotFoundError: Could not find module 'C:\Projecten\ERTMS\python\temp\Library\bin\geos_c.dll' (or one of its dependencies). Try using the full path with constructor syntax

But the package is build in /python/temp/build/exe.wim-amd64-3.9. I have created a folder under temp and copied a geos_c.dll to that location so that the path /python/temp/Library/bin/geos_c.dll did exist, but the executable throws the same error.

These are the packages installed in my conda environment and I am using windows 11.

# packages in environment at \anaconda3\envs\test_env:
#
# Name                    Version                   Build  Channel
altgraph                  0.17               pyhd3eb1b0_0
attrs                     21.2.0             pyhd3eb1b0_0
blas                      1.0                         mkl
bottleneck                1.3.2            py39h7cc1a96_1
brotli                    1.0.9                ha925a31_2
bzip2                     1.0.8                he774522_0
ca-certificates           2021.10.26           haa95532_2
certifi                   2021.10.8        py39haa95532_0
cfitsio                   3.470                he774522_6
click                     7.1.2              pyhd3eb1b0_0
click-plugins             1.1.1              pyhd3eb1b0_0
cligj                     0.7.2            py39haa95532_0
curl                      7.78.0               h86230a5_0
cx-freeze                 6.8.4                    pypi_0    pypi
cx_freeze                 6.8.4            py39hb82d6ee_0    conda-forge
cycler                    0.10.0           py39haa95532_0
expat                     2.4.1                h6c2663c_2
fiona                     1.8.13.post1     py39h758c064_0
fonttools                 4.25.0             pyhd3eb1b0_0
freetype                  2.10.4               hd328e21_0
freexl                    1.0.6                h2bbff1b_0
future                    0.18.2           py39haa95532_1
gdal                      3.0.2            py39hb978731_1
geopandas                 0.9.0                      py_1
geopandas-base            0.9.0                      py_1
geos                      3.8.0                h33f27b4_0
geotiff                   1.6.0                h5770a2b_0
hdf4                      4.2.13               h712560f_2
hdf5                      1.10.6               h7ebc959_0
icc_rt                    2019.0.0             h0cc432a_1
importlib-metadata        4.8.1            py39haa95532_0
intel-openmp              2021.4.0          haa95532_3556
joblib                    1.1.0              pyhd3eb1b0_0
jpeg                      9d                   h2bbff1b_0
kealib                    1.4.14               hde4a422_0
kiwisolver                1.3.1            py39hd77b12b_0
krb5                      1.19.2               h5b6d351_0
libcurl                   7.78.0               h86230a5_0
libgdal                   3.0.2                ha1b3edf_1
libiconv                  1.15                 h1df5818_7
libnetcdf                 4.6.1                hf59b723_4
libpng                    1.6.37               h2a8f88b_0
libpq                     12.2                 hb652d5d_1
libspatialindex           1.9.3                h6c2663c_0
libspatialite             4.3.0a               h7ffb84d_0
libssh2                   1.9.0                h7a1dbc1_1
libtiff                   4.2.0                hd0e1b90_0
libwebp                   1.2.0                h2bbff1b_0
libxml2                   2.9.12               h0ad7f3c_0
lz4-c                     1.9.3                h2bbff1b_1
m2w64-expat               2.1.1                         2
m2w64-gcc-libgfortran     5.3.0                         6
m2w64-gcc-libs            5.3.0                         7
m2w64-gcc-libs-core       5.3.0                         7
m2w64-gettext             0.19.7                        2
m2w64-gmp                 6.1.0                         2
m2w64-libiconv            1.14                          6
m2w64-libwinpthread-git   5.0.0.4634.697f757               2
m2w64-xz                  5.2.2                         2
macholib                  1.14               pyhd3eb1b0_1
mapclassify               2.4.3              pyhd3eb1b0_0
matplotlib-base           3.4.3            py39h49ac443_0
mkl                       2021.4.0           haa95532_640
mkl-service               2.4.0            py39h2bbff1b_0
mkl_fft                   1.3.1            py39h277e83a_0
mkl_random                1.2.2            py39hf11a4ad_0
msys2-conda-epoch         20160418                      1
munch                     2.5.0              pyhd3eb1b0_0
munkres                   1.1.4                      py_0
networkx                  2.6.3              pyhd3eb1b0_0
numexpr                   2.7.3            py39hb80d3ca_1
numpy                     1.21.2           py39hfca59bb_0
numpy-base                1.21.2           py39h0829f74_0
olefile                   0.46               pyhd3eb1b0_0
openjpeg                  2.4.0                h4fc8c34_0
openssl                   1.1.1l               h2bbff1b_0
pandas                    1.3.4            py39h6214cd6_0
pefile                    2019.4.18                  py_0
pillow                    8.4.0            py39hd45dc43_0
pip                       21.2.4           py39haa95532_0
postgresql                12.2                 hb652d5d_1
proj                      6.2.1                h9f7ef89_0
pycryptodome              3.10.1           py39h2bbff1b_0
pyinstaller               3.6              py39h8cc25b3_6
pyparsing                 3.0.4              pyhd3eb1b0_0
pyproj                    2.6.1.post1      py39h593ac45_1
python                    3.9.0                h6244533_2
python-dateutil           2.8.2              pyhd3eb1b0_0
python_abi                3.9                      2_cp39    conda-forge
pytz                      2021.3             pyhd3eb1b0_0
pywin32                   228              py39hbaba5e8_1
pywin32-ctypes            0.2.0           py39haa95532_1000
rtree                     0.9.7            py39h2eaa2aa_1
scikit-learn              1.0.1            py39hf11a4ad_0
scipy                     1.7.1            py39hbe87c03_2
setuptools                58.0.4           py39haa95532_0
shapely                   1.7.1            py39h06580b3_0
six                       1.16.0             pyhd3eb1b0_0
sqlite                    3.36.0               h2bbff1b_0
threadpoolctl             2.2.0              pyh0d69192_0
tiledb                    2.2.9                hf84e3da_0    conda-forge
tk                        8.6.11               h2bbff1b_0
tornado                   6.1              py39h2bbff1b_0
tzdata                    2021e                hda174b7_0
vc                        14.2                 h21ff451_1
vs2015_runtime            14.27.29016          h5e58377_2
wheel                     0.37.0             pyhd3eb1b0_1
wincertstore              0.2              py39haa95532_2
xerces-c                  3.2.3                ha925a31_0
xz                        5.2.5                h62dcd97_0
zipp                      3.6.0              pyhd3eb1b0_0
zlib                      1.2.11               h62dcd97_4
zstd                      1.4.9                h19a0ad4_0

Solution

  • I had the same issues with referencing additional libraries and binary files and using tkinter. I installed geopandas with miniconda (python 3.9) on windows 10. With a lots of try and error I came up with this cx_freeze setup script:

    setup_script.py:

    import os
    from cx_Freeze import setup, Executable
    
    # Dependencies are automatically detected, but it might need fine tuning.
    PYTHON_INSTALL_DIR = r"C:\Users\YOUR_USERNAME\Miniconda3\envs\py39"
    include_files = [(os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'), os.path.join('lib', 'tk86t.dll')),
                     (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'), os.path.join('lib', 'tcl86t.dll')),
                     (os.path.join(PYTHON_INSTALL_DIR, 'Library', 'bin', 'geos_c.dll'), os.path.join('lib', 'geos_c.dll')),
                     (os.path.join(PYTHON_INSTALL_DIR, 'Library', 'bin', 'spatialindex_c-64.dll'), os.path.join('lib', 'spatialindex_c-64.dll')),
                     (os.path.join(PYTHON_INSTALL_DIR, 'Library', 'bin', 'spatialindex-64.dll'), os.path.join('lib', 'spatialindex-64.dll')),
                     (os.path.join(PYTHON_INSTALL_DIR, 'Library', 'share','proj'), os.path.join('lib', 'proj')),
                     (os.path.join(PYTHON_INSTALL_DIR, 'Lib', 'site-packages','pygeos.libs'), os.path.join('lib', 'pygeos.libs'))
                     ]
    
    build_exe_options = {'include_files':include_files, 
                        'bin_includes':[r"C:\Users\YOUR_USERNAME\Miniconda3\envs\py39\Library\bin\geos_c.dll",
                        r"C:\Users\YOUR_USERNAME\Miniconda3\envs\py39\Library\bin\spatialindex_c-64.dll",
                        r"C:\Users\YOUR_USERNAME\Miniconda3\envs\py39\Library\bin\spatialindex-64.dll"]}
    
    # TARGET
    target = Executable(
        script='geopandas_test.py',
        base='Win32GUI' # Tip: disable this line and try to debug your program with the command line first. You might see additional .dll import errors depending on your python installation
    # to debug your .exe and remain the cmd output on your screen, you must start it from command line under windows.
    )
    
    # SETUP CX FREEZE
    setup(
        name = "geopandas_exe_test",
        version = "0.0.1",
        description = "Geopandas on Win 10",
        options={"build_exe": build_exe_options},
        executables = [target]
    )
    

    And in your main script you should include this line os.environ['PROJ_LIB'] = './lib/proj' (This does not work on Win32GUI mode: pyproj.datadir.set_data_dir('./lib/proj'))

    This would be a complete working example with tkinter included:

    geopandas_test.py:

    import pandas as pd
    import tkinter as tk
    
    if (getattr(sys, "frozen", False)):
        print("Frozen State - Running as .exe")
        os.environ['PROJ_LIB'] = './lib/proj' # SET Proj4; alternative with datadir raises error while running as Win32GUI only mode: #datadir.set_data_dir('./lib/proj')
        
        import geopandas as geopd
        import pyproj
    else:
        print("Running in dev mode...")
        import geopandas as geopd
        import pyproj
    
    
    # Some sample Code below:
    df = pd.DataFrame({'City': ['München'],'Latitude': [48],'Longitude': [11.5]})
    gdf = geopd.GeoDataFrame(df, geometry=geopd.points_from_xy(df.Longitude, df.Latitude, crs="EPSG:4326"))
    gdf = gdf.to_crs(25832)
    print(gdf)
    
    window = tk.Tk()
    greeting = tk.Label(text=gdf)
    greeting.pack()
    window.mainloop()
    

    For the sake of completeness. I installed my python environment with following steps:

    conda create --name py39 python=3.9
    conda install geopandas
    pip install --upgrade cx_Freeze
    # DONT INSTALL tk ADDITIONALLY!
    pip uninstall rtree # not sure if this is really neccessary or helps