102 KiB
102 KiB
0. CSV координаты в шейпфайл¶
In [6]:
import geopandas as gpd import pandas as pd import folium import os
In [ ]:
# Считываем проекцию в формате WKT и сохраняем ее в переменной from pyproj import CRSa from shapely.geometry import LineString csv = '4_Маршруты_shapefiles (от Волковой)\\Исходники\\Орнитологический маршрут.csv' df = pd.read_csv(csv, sep=';', decimal=',') file_name = os.path.splitext(os.path.basename(csv))[0] # Создаем геометрию Point из координат geometry = gpd.points_from_xy(df['X'], df['Y']) # Создаем GeoDataFrame Gauss Kruger Zone 7= 20007 gdf = gpd.GeoDataFrame(df, geometry=geometry, crs=32637) gdf.to_file(file_name + '.shp')
In [ ]:
gdf.explore(tiles='Esri.WorldImagery')
МСК координаты в шейп (далее уже в полигон)¶
In [1]:
# Считываем проекцию в формате WKT и сохраняем ее в переменной from pyproj import CRS from shapely.geometry import LineString csv = 'Кладбище поселка Прикубанский.csv' df = pd.read_csv(csv, sep=';', decimal=',') file_name = os.path.splitext(os.path.basename(csv))[0] # Создаем геометрию Point из координат geometry = gpd.points_from_xy(df['Y'], df['X']) # Создаем GeoDataFrame Gauss Kruger Zone 7= 20007 from pyproj import CRS with open(r'F:\PROJECT-Colormap\Преобразования географических проекций (СК, ГСК-2011, МСК)\МСК23 проекции Краснодар\MCK-23_Zone_2N.prj', 'r') as f: msk23_2 = f.read() gdf = gpd.GeoDataFrame(df, geometry=geometry, crs=msk23_2) gdf.to_file(file_name + '.shp')
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[1], line 7 3 from shapely.geometry import LineString 5 csv = 'Кладбище поселка Прикубанский.csv' ----> 7 df = pd.read_csv(csv, sep=';', decimal=',') 9 file_name = os.path.splitext(os.path.basename(csv))[0] 11 # Создаем геометрию Point из координат NameError: name 'pd' is not defined
In [14]:
import geopandas as gpd import pandas as pd from shapely.geometry import Polygon # Читаем CSV df = pd.read_csv('Кладбище поселка Прикубанский.csv', delimiter=';', decimal=',') # Сортируем по столбцу с порядком точек (N) df_sorted = df.sort_values(by='N') # Создаём список координат (X, Y) coords = list(zip(df_sorted['Y'], df_sorted['X'])) # Убедимся, что полигон замкнут (повторяем первую точку в конце) if coords[0] != coords[-1]: coords.append(coords[0]) # Создаём полигон polygon = Polygon(coords) # Создаём GeoDataFrame с одним объектом gdf = gpd.GeoDataFrame(index=[0], geometry=[polygon]) # Создаем GeoDataFrame Gauss Kruger Zone 7= 20007 from pyproj import CRS with open(r'F:\PROJECT-Colormap\Преобразования географических проекций (СК, ГСК-2011, МСК)\МСК23 проекции Краснодар\MCK-23_Zone_2N.prj', 'r') as f: msk23_2 = f.read() gdf.set_crs(msk23_2, inplace=True) # Сохраняем в shapefile gdf.to_file('polygon.shp') print("Shapefile с полигоном успешно сохранён: polygon.shp")
Shapefile с полигоном успешно сохранён: polygon.shp
1. Преобразование файлов (shapefile, DXF) в различные координатные системы (в архивы и по папкам) - ОСНОВНОЕ ПО ООПТ¶
In [2]:
pip install folium
Collecting folium Downloading folium-0.20.0-py2.py3-none-any.whl.metadata (4.2 kB) Collecting branca>=0.6.0 (from folium) Downloading branca-0.8.2-py3-none-any.whl.metadata (1.7 kB) Requirement already satisfied: jinja2>=2.9 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from folium) (3.1.6) Requirement already satisfied: numpy in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from folium) (2.4.2) Requirement already satisfied: requests in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from folium) (2.32.5) Collecting xyzservices (from folium) Downloading xyzservices-2025.11.0-py3-none-any.whl.metadata (4.3 kB) Requirement already satisfied: MarkupSafe>=2.0 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from jinja2>=2.9->folium) (3.0.3) Requirement already satisfied: charset_normalizer<4,>=2 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from requests->folium) (3.4.4) Requirement already satisfied: idna<4,>=2.5 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from requests->folium) (3.11) Requirement already satisfied: urllib3<3,>=1.21.1 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from requests->folium) (2.6.3) Requirement already satisfied: certifi>=2017.4.17 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from requests->folium) (2026.1.4) Downloading folium-0.20.0-py2.py3-none-any.whl (113 kB) Downloading branca-0.8.2-py3-none-any.whl (26 kB) Downloading xyzservices-2025.11.0-py3-none-any.whl (93 kB) Installing collected packages: xyzservices, branca, folium ---------------------------------------- 3/3 [folium] Successfully installed branca-0.8.2 folium-0.20.0 xyzservices-2025.11.0 Note: you may need to restart the kernel to use updated packages.
[notice] A new release of pip is available: 25.3 -> 26.0.1 [notice] To update, run: C:\Users\Lenovo\AppData\Local\Python\pythoncore-3.14-64\python.exe -m pip install --upgrade pip
In [1]:
import geopandas as gpd import pandas as pd import folium import os
In [2]:
# Функция создания списка шейпфайлов для обработки def shp_lst(folder): lst = [] for file in os.listdir(folder): if file.endswith(".shp"): lst.append(os.path.join(folder, file)) print(lst[:2], '\n', f'Всего: {len(lst)} файл(-а, -ов)') return lst
In [3]:
# Считываем проекцию в формате WKT и сохраняем ее в переменной from pyproj import CRS with open(r'c:\ГИС-проекты\2025 - ООПТ\ООПТ из Программы\MSK23_prj\Russia_Cadastre_23_1.prj', 'r') as f: #wkt = f.read() msk23_1 = f.read() # crs = CRS.from_wkt(wkt) with open(r'c:\ГИС-проекты\2025 - ООПТ\ООПТ из Программы\MSK23_prj\Russia_Cadastre_23_2.prj', 'r') as f: msk23_2 = f.read() # msk23_1 = '+proj=tmerc +lat_0=0 +lon_0=37.98333333333 +k=1 +x_0=1300000 +y_0=-4511057.628 +ellps=krass +towgs84=23.57,-140.95,-78.9,0,0,0,0.22 +units=m +no_defs' # msk23_2 = '+proj=tmerc +lat_0=0 +lon_0=40.98333333333 +k=1 +x_0=2300000 +y_0=-4511057.628 +ellps=krass +towgs84=23.57,-140.95,-78.9,0,0,0,0.22 +units=m +no_defs' # Границы районов края со столбцом зон МСК23 gdf_boundary = gpd.read_file(r'c:\ГИС-проекты\2025 - ООПТ\ООПТ из Программы\Слои КК\Borders-adm-MSK-Zone.shp') # Преобразуем в WGS84 gdf_boundary = gdf_boundary.to_crs(4326) gdf_boundary.crs
Out[3]:
<Geographic 2D CRS: EPSG:4326> Name: WGS 84 Axis Info [ellipsoidal]: - Lat[north]: Geodetic latitude (degree) - Lon[east]: Geodetic longitude (degree) Area of Use: - name: World. - bounds: (-180.0, -90.0, 180.0, 90.0) Datum: World Geodetic System 1984 ensemble - Ellipsoid: WGS 84 - Prime Meridian: Greenwich
In [4]:
# Создадим два фрейма с зонам МСК23 в проекции 4326 (для простоты) msk23_1_boundary = gdf_boundary[gdf_boundary['MSK23_Zone']==1].union_all() msk23_1_boundary = gpd.GeoDataFrame({'geometry': [msk23_1_boundary]}, crs=gdf_boundary.crs) msk23_2_boundary = gdf_boundary[gdf_boundary['MSK23_Zone']==2].union_all() msk23_2_boundary = gpd.GeoDataFrame({'geometry': [msk23_2_boundary]}, crs=gdf_boundary.crs)
In [5]:
# Словарь для создания папки для района rayon_dic = { 'esk': 'Ейский район', 'kvk': 'Кавказский район', 'kln': 'Калининский район', 'knv': 'Каневской район', 'krl': 'Крыловский район', 'krm': 'Крымский район', 'ksh': 'Кущевский район', 'mst': 'Мостовской район', 'nvk': 'Новокубанский район', 'pvl': 'Павловский район', 'tbl': 'Тбилисский район', 'tps': 'Туапсинский район', 'usp': 'Успенский район', 'ulb': 'Усть-Лабинский район', 'anp': 'Анапский район', 'brh': 'Брюховецкий район', 'glj': 'Геленджикский район', 'nvp': 'Новопокровский район', 'nvr': 'Новороссийск', 'otr': 'Отрадненский район', 'sch': 'Сочи', 'svr': 'Северский район', 'tmr': 'Темрюкский район', 'shr': 'Щербиновский район' }
In [6]:
folder = r'C:\ГИС-проекты\2025 - ООПТ\ООПТ из Программы\Input_processing' shp_lst = shp_lst(folder)
['C:\\ГИС-проекты\\2025 - ООПТ\\ООПТ из Программы\\Input_processing\\nvr-vasilevskie_osypi_pp_orz-new.shp'] Всего: 1 файл(-а, -ов)
In [29]:
pip install ezdxf
Collecting ezdxf Downloading ezdxf-1.4.3-py3-none-any.whl.metadata (9.9 kB) Collecting pyparsing>=2.0.1 (from ezdxf) Downloading pyparsing-3.3.2-py3-none-any.whl.metadata (5.8 kB) Requirement already satisfied: typing_extensions>=4.6.0 in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from ezdxf) (4.15.0) Requirement already satisfied: numpy in c:\users\lenovo\appdata\local\python\pythoncore-3.14-64\lib\site-packages (from ezdxf) (2.4.2) Collecting fonttools (from ezdxf) Downloading fonttools-4.61.1-cp314-cp314-win_amd64.whl.metadata (116 kB) Downloading ezdxf-1.4.3-py3-none-any.whl (1.3 MB) ---------------------------------------- 0.0/1.3 MB ? eta -:--:-- ---------------------------------------- 0.0/1.3 MB ? eta -:--:-- ---------------------------------------- 0.0/1.3 MB ? eta -:--:-- ------- -------------------------------- 0.3/1.3 MB ? eta -:--:-- ------- -------------------------------- 0.3/1.3 MB ? eta -:--:-- --------------- ------------------------ 0.5/1.3 MB 663.7 kB/s eta 0:00:02 --------------- ------------------------ 0.5/1.3 MB 663.7 kB/s eta 0:00:02 ----------------------- ---------------- 0.8/1.3 MB 626.9 kB/s eta 0:00:01 ----------------------- ---------------- 0.8/1.3 MB 626.9 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ------------------------------- -------- 1.0/1.3 MB 588.7 kB/s eta 0:00:01 ---------------------------------------- 1.3/1.3 MB 371.6 kB/s 0:00:03 Downloading pyparsing-3.3.2-py3-none-any.whl (122 kB) Downloading fonttools-4.61.1-cp314-cp314-win_amd64.whl (2.3 MB) ---------------------------------------- 0.0/2.3 MB ? eta -:--:-- ---------------------------------------- 0.0/2.3 MB ? eta -:--:-- ---------------------------------------- 0.0/2.3 MB ? eta -:--:-- ---- ----------------------------------- 0.3/2.3 MB ? eta -:--:-- ---- ----------------------------------- 0.3/2.3 MB ? eta -:--:-- ---- ----------------------------------- 0.3/2.3 MB ? eta -:--:-- --------- ------------------------------ 0.5/2.3 MB 389.7 kB/s eta 0:00:05 --------- ------------------------------ 0.5/2.3 MB 389.7 kB/s eta 0:00:05 --------- ------------------------------ 0.5/2.3 MB 389.7 kB/s eta 0:00:05 ------------- -------------------------- 0.8/2.3 MB 463.1 kB/s eta 0:00:04 ------------- -------------------------- 0.8/2.3 MB 463.1 kB/s eta 0:00:04 ------------- -------------------------- 0.8/2.3 MB 463.1 kB/s eta 0:00:04 ------------------ --------------------- 1.0/2.3 MB 432.6 kB/s eta 0:00:03 ------------------ --------------------- 1.0/2.3 MB 432.6 kB/s eta 0:00:03 ------------------ --------------------- 1.0/2.3 MB 432.6 kB/s eta 0:00:03 ---------------------- ----------------- 1.3/2.3 MB 436.2 kB/s eta 0:00:03 ---------------------- ----------------- 1.3/2.3 MB 436.2 kB/s eta 0:00:03 ---------------------- ----------------- 1.3/2.3 MB 436.2 kB/s eta 0:00:03 --------------------------- ------------ 1.6/2.3 MB 431.0 kB/s eta 0:00:02 --------------------------- ------------ 1.6/2.3 MB 431.0 kB/s eta 0:00:02 ------------------------------- -------- 1.8/2.3 MB 447.5 kB/s eta 0:00:02 ------------------------------- -------- 1.8/2.3 MB 447.5 kB/s eta 0:00:02 ------------------------------- -------- 1.8/2.3 MB 447.5 kB/s eta 0:00:02 ------------------------------- -------- 1.8/2.3 MB 447.5 kB/s eta 0:00:02 ------------------------------------ --- 2.1/2.3 MB 421.3 kB/s eta 0:00:01 ------------------------------------ --- 2.1/2.3 MB 421.3 kB/s eta 0:00:01 ------------------------------------ --- 2.1/2.3 MB 421.3 kB/s eta 0:00:01 ---------------------------------------- 2.3/2.3 MB 421.1 kB/s 0:00:05 Installing collected packages: pyparsing, fonttools, ezdxf ------------- -------------------------- 1/3 [fonttools] ------------- -------------------------- 1/3 [fonttools] ------------- -------------------------- 1/3 [fonttools] ------------- -------------------------- 1/3 [fonttools] ------------- -------------------------- 1/3 [fonttools] ------------- -------------------------- 1/3 [fonttools] -------------------------- ------------- 2/3 [ezdxf] -------------------------- ------------- 2/3 [ezdxf] -------------------------- ------------- 2/3 [ezdxf] -------------------------- ------------- 2/3 [ezdxf] -------------------------- ------------- 2/3 [ezdxf] -------------------------- ------------- 2/3 [ezdxf] -------------------------- ------------- 2/3 [ezdxf] ---------------------------------------- 3/3 [ezdxf] Successfully installed ezdxf-1.4.3 fonttools-4.61.1 pyparsing-3.3.2 Note: you may need to restart the kernel to use updated packages.
WARNING: The scripts fonttools.exe, pyftmerge.exe, pyftsubset.exe and ttx.exe are installed in 'C:\Users\Lenovo\AppData\Local\Python\pythoncore-3.14-64\Scripts' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. WARNING: The script ezdxf.exe is installed in 'C:\Users\Lenovo\AppData\Local\Python\pythoncore-3.14-64\Scripts' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. [notice] A new release of pip is available: 25.3 -> 26.0.1 [notice] To update, run: C:\Users\Lenovo\AppData\Local\Python\pythoncore-3.14-64\python.exe -m pip install --upgrade pip
In [9]:
### Сохраняет файлы в папку с именем района, которая создается автоматически по трем первым буквам шейп-файла import ezdxf import os import zipfile import fiona # Функция для создания архива шейпа в той же папке # filepath - путь к файлу, outfolder - папка для сохранения def zip_shp(filepath, outfolder): filename = os.path.splitext(os.path.basename(filepath))[0] folder = os.path.dirname(filepath) with zipfile.ZipFile(outfolder + "//" + f'{filename}.zip', 'w') as zip_file: # Ищем файлы с одинаковым именем, но с разным расширением for file in os.listdir(folder): if os.path.splitext(file)[0] == filename: # Добавляем файл в архив zip_file.write(os.path.join(folder, file), file) # Удаляет файл os.remove(os.path.join(folder, file))
In [5]:
pip install GDAL-3.9.1-cp314-cp314-win_amd64.whl pip install fiona-1.10.1-cp314-cp314-win_amd64.whl
Processing c:\гис-проекты\fiona-1.10.1-cp314-cp314-win_amd64.whl Note: you may need to restart the kernel to use updated packages.
WARNING: Requirement 'fiona-1.10.1-cp314-cp314-win_amd64.whl' looks like a filename, but the file does not exist ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory: 'C:\\ГИС-проекты\\fiona-1.10.1-cp314-cp314-win_amd64.whl' [notice] A new release of pip is available: 25.3 -> 26.0.1 [notice] To update, run: C:\Users\Lenovo\AppData\Local\Python\pythoncore-3.14-64\python.exe -m pip install --upgrade pip
In [11]:
# Временная папка temp = 'temp' os.makedirs(temp, exist_ok=True) # Преобразуем все файлы в проекцию WGS84 и сохраняем в формате shape и DVG for shp in shp_lst: gdf = gpd.read_file(shp) # Создать папку для района outfolder = rayon_dic[os.path.basename(shp)[:3]] # берет первый три буквы названия файла и достает из словаря полное название района os.makedirs(outfolder, exist_ok=True) ### WGS84 gdf = gdf.to_crs(4326) filepath = os.path.join(temp, os.path.splitext(os.path.basename(shp))[0]) + '_WGS84' + '.shp' gdf.to_file(filename=filepath, driver='ESRI Shapefile') zip_shp(filepath, outfolder) # DXF filepath = os.path.join(outfolder, os.path.splitext(os.path.basename(shp))[0]) + '_WGS84' + '.dxf' #gdf.geometry.to_file(filepath, driver="DXF") gdf.geometry.to_file(filepath, driver="DXF", engine='fiona') # Раздел с МСК23 (распределение по зонам) # Для определения делаем центроид полигона, если он внутри зоны МСК1, то это зона 1, если нет, то зона 2 (предварительно пришлось преобразовать WGS84 в Меркатор - не работало) # if gdf.to_crs(3857).geometry.centroid.intersects(msk23_1_boundary).any(): if gdf.geometry.centroid.intersects(msk23_1_boundary).any(): gdf = gdf.to_crs(msk23_1) filepath = os.path.join(temp, os.path.splitext(os.path.basename(shp))[0]) + '_MSK23_1' + '.shp' gdf.to_file(filename=filepath, driver='ESRI Shapefile') zip_shp(filepath, outfolder) # DXF filepath = os.path.join(outfolder, os.path.splitext(os.path.basename(shp))[0]) + '_MSK23_1' + '.dxf' gdf.geometry.to_file(filepath, driver="DXF") else: gdf = gdf.to_crs(msk23_2) filepath = os.path.join(temp, os.path.splitext(os.path.basename(shp))[0]) + '_MSK23_2' + '.shp' gdf.to_file(filename=filepath, driver='ESRI Shapefile') zip_shp(filepath, outfolder) # DXF filepath = os.path.join(outfolder, os.path.splitext(os.path.basename(shp))[0]) + '_MSK23_2' + '.dxf' gdf.geometry.to_file(filepath, driver="DXF") # with tempfile.TemporaryDirectory() as temp_dir: # temp_dir = Path(temp_dir) # localFile = 'myshapefile' # gdf.to_file(filename=temp_dir, driver='ESRI Shapefile') # archiveFile = shutil.make_archive(localFile, 'zip', temp_dir) # shutil.rmtree(temp_dir) # # Преобразуем данные в формат DWG-DXF # filepath = os.path.join(outfolder, os.path.splitext(os.path.basename(shp))[0]) + '.dxf' # # Create a new DXF document # doc = ezdxf.new(dxfversion='R2010') # # Add a new layer to the DXF document # msp = doc.modelspace() # # Iterate through the GeoDataFrame and add geometries to the DXF # for _, row in gdf.iterrows(): # if row.geometry.type == 'Point': # msp.add_point((row.geometry.x, row.geometry.y)) # elif row.geometry.type in ['LineString', 'MultiLineString']: # for line in row.geometry: # msp.add_lwpolyline(line.coords) # elif row.geometry.type in ['Polygon', 'MultiPolygon']: # for poly in row.geometry: # coords = list(poly.exterior.coords) # coords.append(coords[0]) # добавляем последнюю точку координат к началу списка координат # msp.add_lwpolyline(coords) # # Save the DXF file # doc.saveas(filepath)
C:\Users\Lenovo\AppData\Local\Temp\ipykernel_14212\3067829712.py:25: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation. if gdf.geometry.centroid.intersects(msk23_1_boundary).any():
Преобразование файлов в DXF¶
In [87]:
for shp in shp_lst: gdf = gpd.read_file(shp) # Создать папку для района outfolder = rayon_dic[os.path.basename(shp)[:3]] # берет первый три буквы названия файла и достает из словаря полное название района os.makedirs(outfolder, exist_ok=True) # DXF filepath = os.path.join(outfolder, os.path.splitext(os.path.basename(shp))[0]) + '.dxf' gdf.geometry.to_file(filepath, driver="DXF")
!!!!! ДАЛЕЕ НУЖНО РАЗБИРАТЬСЯ https://mapscaping.com/shapefile-to-dxf/
In [90]:
import geopandas as gpd import ezdxf # Load the Shapefile gdf = gpd.read_file('input.shp') # Create a new DXF document doc = ezdxf.new(dxfversion='R2010') # Add a new layer to the DXF document msp = doc.modelspace() # Iterate through the GeoDataFrame and add geometries to the DXF for _, row in gdf.iterrows(): if row.geometry.type == 'Point': msp.add_point((row.geometry.x, row.geometry.y)) elif row.geometry.type in ['LineString', 'MultiLineString']: for line in row.geometry: msp.add_lwpolyline(line.coords) elif row.geometry.type in ['Polygon', 'MultiPolygon']: for poly in row.geometry: msp.add_lwpolyline(poly.exterior.coords, is_closed=True) # Save the DXF file doc.saveas('output.dxf')
--------------------------------------------------------------------------- DataSourceError Traceback (most recent call last) Cell In[90], line 5 2 import ezdxf 4 # Load the Shapefile ----> 5 gdf = gpd.read_file('input.shp') 7 # Create a new DXF document 8 doc = ezdxf.new(dxfversion='R2010') File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\io\file.py:299, in _read_file(filename, bbox, mask, columns, rows, engine, **kwargs) 296 from_bytes = True 298 if engine == "pyogrio": --> 299 return _read_file_pyogrio( 300 filename, bbox=bbox, mask=mask, columns=columns, rows=rows, **kwargs 301 ) 303 elif engine == "fiona": 304 if pd.api.types.is_file_like(filename): File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\io\file.py:549, in _read_file_pyogrio(path_or_bytes, bbox, mask, rows, **kwargs) 540 warnings.warn( 541 "The 'include_fields' and 'ignore_fields' keywords are deprecated, and " 542 "will be removed in a future release. You can use the 'columns' keyword " (...) 545 stacklevel=3, 546 ) 547 kwargs["columns"] = kwargs.pop("include_fields") --> 549 return pyogrio.read_dataframe(path_or_bytes, bbox=bbox, **kwargs) File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\pyogrio\geopandas.py:265, in read_dataframe(path_or_buffer, layer, encoding, columns, read_geometry, force_2d, skip_features, max_features, where, bbox, mask, fids, sql, sql_dialect, fid_as_index, use_arrow, on_invalid, arrow_to_pandas_kwargs, **kwargs) 260 if not use_arrow: 261 # For arrow, datetimes are read as is. 262 # For numpy IO, datetimes are read as string values to preserve timezone info 263 # as numpy does not directly support timezones. 264 kwargs["datetime_as_string"] = True --> 265 result = read_func( 266 path_or_buffer, 267 layer=layer, 268 encoding=encoding, 269 columns=columns, 270 read_geometry=read_geometry, 271 force_2d=gdal_force_2d, 272 skip_features=skip_features, 273 max_features=max_features, 274 where=where, 275 bbox=bbox, 276 mask=mask, 277 fids=fids, 278 sql=sql, 279 sql_dialect=sql_dialect, 280 return_fids=fid_as_index, 281 **kwargs, 282 ) 284 if use_arrow: 285 meta, table = result File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\pyogrio\raw.py:198, in read(path_or_buffer, layer, encoding, columns, read_geometry, force_2d, skip_features, max_features, where, bbox, mask, fids, sql, sql_dialect, return_fids, datetime_as_string, **kwargs) 59 """Read OGR data source into numpy arrays. 60 61 IMPORTANT: non-linear geometry types (e.g., MultiSurface) are converted (...) 194 195 """ 196 dataset_kwargs = _preprocess_options_key_value(kwargs) if kwargs else {} --> 198 return ogr_read( 199 get_vsi_path_or_buffer(path_or_buffer), 200 layer=layer, 201 encoding=encoding, 202 columns=columns, 203 read_geometry=read_geometry, 204 force_2d=force_2d, 205 skip_features=skip_features, 206 max_features=max_features or 0, 207 where=where, 208 bbox=bbox, 209 mask=_mask_to_wkb(mask), 210 fids=fids, 211 sql=sql, 212 sql_dialect=sql_dialect, 213 return_fids=return_fids, 214 dataset_kwargs=dataset_kwargs, 215 datetime_as_string=datetime_as_string, 216 ) File pyogrio\\_io.pyx:1240, in pyogrio._io.ogr_read() File pyogrio\\_io.pyx:220, in pyogrio._io.ogr_open() DataSourceError: input.shp: No such file or directory
In [ ]:
# Для проекции в МСК # В начале определяем зону по файлу с районами, далее преобразуем в эту зону # НЕ ДОДЕЛАН!!!! for shp in shp_lst[:3]: gdf = gpd.read_file(shp).union_all centroid = gdf.centroid centroid_gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(centroid.x, centroid.y)).set_crs(3857) gdf_joined = gpd.sjoin( gdf = gdf.to_crs(msk23_1) filepath = os.path.join(outfolder, os.path.basename(shp)) gdf.to_file(filepath) # Преобразуем данные в формат DWG-DXF import osgeo import ogr driver = ogr.GetDriverByName('DXF') ds = driver.CreateCopy('output.dxf', gdf) ds = None import pydxf dxf = pydxf.DxfFile('input.dxf') # Преобразуем DXF в DWG dwg = pydxf.DwfFile('output.dwg') dwg.write(dxf)
In [ ]:
import pyproj x, y = gdf.centroid.x, gdf.centroid.y lon, lat = pyproj.transform(pyproj.Proj('epsg:3857'), pyproj.Proj('epsg:4326'), x, y)
In [ ]:
# Определите зону MSK if central_meridian >= 45 and central_meridian < 48: print("Проекция: MSK-23.1") elif central_meridian >= 48 and central_meridian <= 51: print("Проекция: MSK-23.2") else: print("Проекция: MSK, но зона не определена")
In [ ]:
centroid = gdf.centroid gdf_joined = gpd.sjoin(centroid, gdf, how='inner', op='within').explore()
In [1]:
centroid_gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(centroid.x, centroid.y)).set_crs(msk23_1)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[1], line 1 ----> 1 centroid_gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(centroid.x, centroid.y)).set_crs(msk23_1) NameError: name 'gpd' is not defined
2. Создание таблицы с координатами (поворотные точки) ТОЛЬКО МСК23¶
In [9]:
shp = '11111\\sch-aibga-grushevn_pp_orz-new_MSK23_2.shp' # Извлекаем имя файла shp_name = os.path.splitext(os.path.basename(shp))[0] gdf = gpd.read_file(shp) # Извлекаем координаты вертексов полигона (формат X, Y в текущей координатной системе) df = gdf.get_coordinates() # Добавляем индекс к таблице df.reset_index(inplace = True, drop = True) # Первая строка с 1 df.index += 1 df = df.reset_index() # Округляем df = round(df) # Переименовываем столбцы df.columns = ['N', 'X_MSK23', 'Y_MSK23'] # Добавляет первую строку в конец таблицы df = df.take(list(range(len(df) - 1)) + [0]) df.to_excel(shp_name + '.xlsx', index=False)
--------------------------------------------------------------------------- DataSourceError Traceback (most recent call last) Cell In[9], line 4 2 # Извлекаем имя файла 3 shp_name = os.path.splitext(os.path.basename(shp))[0] ----> 4 gdf = gpd.read_file(shp) 6 # Извлекаем координаты вертексов полигона (формат X, Y в текущей координатной системе) 7 df = gdf.get_coordinates() File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\io\file.py:299, in _read_file(filename, bbox, mask, columns, rows, engine, **kwargs) 296 from_bytes = True 298 if engine == "pyogrio": --> 299 return _read_file_pyogrio( 300 filename, bbox=bbox, mask=mask, columns=columns, rows=rows, **kwargs 301 ) 303 elif engine == "fiona": 304 if pd.api.types.is_file_like(filename): File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\io\file.py:549, in _read_file_pyogrio(path_or_bytes, bbox, mask, rows, **kwargs) 540 warnings.warn( 541 "The 'include_fields' and 'ignore_fields' keywords are deprecated, and " 542 "will be removed in a future release. You can use the 'columns' keyword " (...) 545 stacklevel=3, 546 ) 547 kwargs["columns"] = kwargs.pop("include_fields") --> 549 return pyogrio.read_dataframe(path_or_bytes, bbox=bbox, **kwargs) File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\pyogrio\geopandas.py:265, in read_dataframe(path_or_buffer, layer, encoding, columns, read_geometry, force_2d, skip_features, max_features, where, bbox, mask, fids, sql, sql_dialect, fid_as_index, use_arrow, on_invalid, arrow_to_pandas_kwargs, **kwargs) 260 if not use_arrow: 261 # For arrow, datetimes are read as is. 262 # For numpy IO, datetimes are read as string values to preserve timezone info 263 # as numpy does not directly support timezones. 264 kwargs["datetime_as_string"] = True --> 265 result = read_func( 266 path_or_buffer, 267 layer=layer, 268 encoding=encoding, 269 columns=columns, 270 read_geometry=read_geometry, 271 force_2d=gdal_force_2d, 272 skip_features=skip_features, 273 max_features=max_features, 274 where=where, 275 bbox=bbox, 276 mask=mask, 277 fids=fids, 278 sql=sql, 279 sql_dialect=sql_dialect, 280 return_fids=fid_as_index, 281 **kwargs, 282 ) 284 if use_arrow: 285 meta, table = result File ~\AppData\Local\Programs\Python\Python310\lib\site-packages\pyogrio\raw.py:198, in read(path_or_buffer, layer, encoding, columns, read_geometry, force_2d, skip_features, max_features, where, bbox, mask, fids, sql, sql_dialect, return_fids, datetime_as_string, **kwargs) 59 """Read OGR data source into numpy arrays. 60 61 IMPORTANT: non-linear geometry types (e.g., MultiSurface) are converted (...) 194 195 """ 196 dataset_kwargs = _preprocess_options_key_value(kwargs) if kwargs else {} --> 198 return ogr_read( 199 get_vsi_path_or_buffer(path_or_buffer), 200 layer=layer, 201 encoding=encoding, 202 columns=columns, 203 read_geometry=read_geometry, 204 force_2d=force_2d, 205 skip_features=skip_features, 206 max_features=max_features or 0, 207 where=where, 208 bbox=bbox, 209 mask=_mask_to_wkb(mask), 210 fids=fids, 211 sql=sql, 212 sql_dialect=sql_dialect, 213 return_fids=return_fids, 214 dataset_kwargs=dataset_kwargs, 215 datetime_as_string=datetime_as_string, 216 ) File pyogrio\\_io.pyx:1240, in pyogrio._io.ogr_read() File pyogrio\\_io.pyx:220, in pyogrio._io.ogr_open() DataSourceError: 11111\sch-aibga-grushevn_pp_orz-new_MSK23_2.shp: No such file or directory
3. Поворотные точки градусы, минуты, секунды в ГСК 2011 (для проверки по ГЕОЛОГИИ)¶
In [1]:
import geopandas as gpd import pandas as pd from os.path import splitext import os
Функции¶
In [2]:
# Функция создания списка шейпфайлов для обработки def shp_lst(folder): lst = [] for file in os.listdir(folder): if file.endswith(".shp"): lst.append(os.path.join(folder, file)) print(lst[:2], '\n', f'Всего: {len(lst)} файл(-а, -ов)') return lst
In [3]:
# Создает таблицу с координатами градусы-минуты-секунды для полигона в GeoDataFrame (должен быть один объект) def coordinates(gdf): df = gdf.get_coordinates() # Добавляем индекс к таблице df.reset_index(inplace = True, drop = True) # Первая строка с 1 df.index += 1 df = df.reset_index() df['latitude_deg'] = df['y'].astype(int) df['latitude_min_full'] = ((df['y'] - df['latitude_deg']) * 60).abs() df['latitude_min'] = df['latitude_min_full'].astype(int) df['latitude_sec'] = round((df['latitude_min_full'] - df['latitude_min']) * 60, 3) df['longitude_deg'] = df['x'].astype(int) df['longitude_min_full'] = ((df['x'] - df['longitude_deg']) * 60).abs() df['longitude_min'] = df['longitude_min_full'].astype(int) df['longitude_sec'] = round((df['longitude_min_full'] - df['longitude_min']) * 60, 3) df.drop(['x', 'y', 'latitude_min_full', 'longitude_min_full'], axis=1, inplace=True) df = df.rename(columns={'index': 'N'}) # Добавляем в конец дубликат первой строки df = df.take(list(range(len(df) - 1)) + [0]) return df
In [4]:
##### Функция шаблона Excel c шапкой для координат ГСК-2011 from openpyxl import Workbook from openpyxl.styles import Alignment, Font, Border, Side def template_gc(output_file): wb = Workbook() ws = wb.active # --- Первая строка: заголовок "ГСК-2011", объединённый над всеми столбцами --- max_col = 7 # например, у вас 7 столбцов в шапке ниже ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=max_col) header_cell = ws.cell(row=1, column=1, value='ГСК-2011') header_cell.font = Font(bold=False) # простой шрифт (без жирности) header_cell.alignment = Alignment(horizontal='center', vertical='center') # --- Вторая и третья строки: многоуровневая шапка --- # Вторая строка — первый уровень шапки ws['A3'] = "№ точки" ws['B3'] = "Широта [-90:90]\n(С.Ш. - положительные\nЮ.Ш. - отрицательные)" ws['E3'] = "Долгота [-180:180]\n(В.Д. - положительные\nЗ.Д. - отрицательные)" # Объединение ячеек для первого уровня заголовков ws.merge_cells('A3:A4') # "№ точки" занимает две строки вертикально ws.merge_cells('B3:D3') # "Широта ..." над 3 колонками ws.merge_cells('E3:G3') # "Долгота ..." над 3 колонками # Третья строка — второй уровень (подзаголовки) с обычным шрифтом sub_headers = [ 'Градусы, \nцелое', 'Минуты, \nцелое', 'Секунды,\nдробь до \n3 знаков' ] cols_lat = ['B', 'C', 'D'] cols_lon = ['E', 'F', 'G'] for col, text in zip(cols_lat, sub_headers): c = ws[f"{col}4"] c.value = text c.font = Font(bold=False) # простой шрифт c.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) for col, text in zip(cols_lon, sub_headers): c = ws[f"{col}4"] c.value = text c.font = Font(bold=False) c.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) # Выравнивание и жирность для второго уровня заголовка (строка 2) for cell in ['A3', 'B3', 'E3']: c = ws[cell] c.font = Font(bold=True) c.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) # Установим высоту строк для переносов текста ws.row_dimensions[1].height = 20 ws.row_dimensions[2].height = 8 ws.row_dimensions[3].height = 48 ws.row_dimensions[4].height = 40 # Подгонка ширины столбцов col_widths = [10, 15, 15, 20, 15, 15, 20] for i, width in enumerate(col_widths, 1): ws.column_dimensions[chr(64 + i)].width = width # Добавление рамки по всей области шапки (с 1 по 3 строку, с A по G) thin_side = Side(border_style="thin", color="000000") border = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side) for row in ws.iter_rows(min_row=3, max_row=4, min_col=1, max_col=max_col): for cell in row: # Центрируем все ячейки по горизонтали и вертикали, если текст в ячейке есть if cell.value is not None: cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) # Устанавливаем рамку cell.border = border # Сохраняем файл wb.save(output_file)
In [5]:
# Функция форматирования файла Excel, выравнивание всех ячеек по центру и добавления первой строки с номером участка from openpyxl import load_workbook from openpyxl.styles import Alignment, Font, Border, Side def format_xlsx(xlsx, counter): # counter - текущий номер участка # Открываем существующий файл wb = load_workbook(xlsx) ws = wb.active # или wb['SheetName'] # Определяем размер текущей таблицы min_row, max_row = ws.min_row, ws.max_row min_col, max_col = ws.min_column, ws.max_column # УБРАЛ БЛОК ДОБАВЛЕНИЯ ПЕРВОЙ СТРОКИ С НОМЕРОМ УЧАСТКА, ТАК КАК В СИСТЕМУ МОЖНО ЗАГРУЖАТЬ ТОЛЬКО ПО ОДНОМУ УЧАСТКУ # # Вставляем пустую первую строку # ws.insert_rows(1) # max_row += 1 # сдвигаем нижнюю границу таблицы вниз на 1, т.к. вставили строку # # Объединяем все ячейки первой строки в одну ячейку # merge_range = f"{ws.cell(row=1, column=min_col).coordinate}:" \ # f"{ws.cell(row=1, column=max_col).coordinate}" # ws.merge_cells(merge_range) # header_cell = ws.cell(row=1, column=min_col) # header_cell.value = "Участок " + str(counter) # header_cell.font = Font(bold=True, size=11) # header_cell.alignment = Alignment(horizontal='center', vertical='center') # # Устанавливаем высоту первой строки для читаемости # ws.row_dimensions[1].height = 20 # Создаем стиль тонкой рамки thin_side = Side(border_style="thin", color="000000") thin_border = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side) # Проходим по всем ячейкам таблицы (включая новую первую строку) for row in ws.iter_rows(min_row=0, max_row=max_row, min_col=min_col, max_col=max_col): for cell in row: # Центрируем содержимое ячеек if cell.value is not None: cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True) # Устанавливаем тонкую рамку в каждую ячейку cell.border = thin_border # Сохраняем в тот же файл wb.save(xlsx)
In [6]:
# Функция объединения 2+ файлов-участков от одного ООПТ from openpyxl import load_workbook, Workbook from openpyxl.styles import Font, PatternFill, Border, Alignment, Protection def copy_cell_style(src_cell, tgt_cell): if src_cell.has_style: tgt_cell.font = src_cell.font.copy() tgt_cell.border = src_cell.border.copy() tgt_cell.fill = src_cell.fill.copy() tgt_cell.number_format = src_cell.number_format tgt_cell.protection = src_cell.protection.copy() tgt_cell.alignment = src_cell.alignment.copy() def concat_xlsx_with_style(files, output_file): wb_new = Workbook() ws_new = wb_new.active ws_new.title = "Угловые точки DD⁰MM'SS.SS" current_row = 1 for idx, file in enumerate(files): wb_src = load_workbook(file) ws_src = wb_src.active min_row = 1 # Можно поменять на 2 для пропуска заголовков во 2-м и последующих файлах # Копируем значения и стили по строкам for r in range(min_row, ws_src.max_row + 1): for c in range(1, ws_src.max_column + 1): src_cell = ws_src.cell(row=r, column=c) tgt_cell = ws_new.cell(row=current_row, column=c) tgt_cell.value = src_cell.value copy_cell_style(src_cell, tgt_cell) current_row += 1 # Копируем объединённые ячейки (merged cells) с учётом сдвига строк row_offset = current_row - (ws_src.max_row + 1) for merged_range in ws_src.merged_cells.ranges: start_row = merged_range.min_row + row_offset end_row = merged_range.max_row + row_offset start_col = merged_range.min_col end_col = merged_range.max_col new_range = f"{ws_src.cell(row=start_row, column=start_col).coordinate}:" \ f"{ws_src.cell(row=end_row, column=end_col).coordinate}" try: ws_new.merge_cells(new_range) except Exception as e: print(f"Ошибка объединения: {new_range} - {e}") # Копирование высоты строк с учётом сдвига for r in range(min_row, ws_src.max_row + 1): height = ws_src.row_dimensions[r].height if height is not None: ws_new.row_dimensions[r + row_offset].height = height wb_new.save(output_file)
In [7]:
# Функция добавления листа с координатной системой СК-2011 для добавления на сайт с геологической информацией https://efgi.ru/ from openpyxl import load_workbook from openpyxl.utils import get_column_letter def add_cs(excel_path): # Данные для записи headers = ["СК", "EPSG", "Код в Proj4"] values = [ "ГСК-2011", 7683, "+proj=longlat +a=6378136.5 +rf=298.2564151 " "+towgs84=0.013,-0.092,-0.03,-0.001738,0.003559,-0.004263,0.0074 +no_defs" ] # Загружаем книгу wb = load_workbook(excel_path) # Если лист с именем "СК" существует - удаляем его (чтобы заменить) if "СК" in wb.sheetnames: del wb["СК"] # Создаём новый лист "СК" ws = wb.create_sheet("СК") # Записываем заголовки в первую строку for col_num, header in enumerate(headers, start=1): cell = ws.cell(row=1, column=col_num, value=header) # Записываем данные во вторую строку for col_num, value in enumerate(values, start=1): ws.cell(row=2, column=col_num, value=value) # Сохраняем файл wb.save(excel_path)
In [8]:
# Словарь с названиями районов и ООПТ dic_oopt = { 'anp-anap-soch-skal_pp_orz-new_WGS84': 'Анапа_Анапские сочащиеся скалы', 'anp-stepi-balki-zozulina_pp_orz-new_WGS84': 'Анапа_Степи балки Зозулина', 'anp-bujor-kodzor-les_orz-new_WGS84': 'Анапа_ПП Бужоро-Кодзорский лес', 'anp-krasnogor-les_pp_orz-new_WGS84': 'Анапа_ПП Красногорский лес', 'anp-kumatyr-hrebet_zak_orz_WGS84': 'Анапа_ПП Куматырский лес', 'anp-shkurat-les_orz-new_WGS84': 'Анапа_ПП Шкуратский лес', 'anp-yug-gostag-les_orz-new_WGS84': 'Анапа_ПП Южно-Гостагаевскй лес', 'anp-kurb-bugor_pp_orz-new_WGS84': 'Анапа_ПП Курбацкие бугры', 'anp-chekon-redkoles_orz-new_WGS84': 'Анапа_ПП Чеконское редколесье', 'brh-vys-bereg-beysug_pp_orz-new_WGS84': 'Брюховецкий_район_ПП_Высокий берег реки Бейсуг', 'glj-gebeus-gorn-mass_zak_orz-new_WGS84': 'Геленджик_ПП_Гебеусский горный массив', 'glj-mojj-redkles-gor-neksis_pp_orz-new_WGS84': 'Геленджик_ПП_Можжевеловые редколесья хребта Нексис', 'esk-voronts-step_pp_orz-new_WGS84': 'Ейский район_Воронцовская степь', 'esk-kruglaya-pad_pp_orz-new_WGS84': 'Ейский район_Круглая падь', 'esk-hans-yasen_zak_orz-new_WGS84': 'Ейский район_Ханско-Ясенский заказник', 'esk-yasnopol-step_pp_orz-new_WGS84': 'Ейский район_Яснопольская_степь', 'kvk-obrivistiy-bereg-reki-kuban_pp_orz-new_WGS84': 'Кавказский и Тбилисск район_Обрывистый берег реки Кубань', 'kvk-temijbek-vysok-bereg_pp_orz-new_WGS84': 'Кавказский_район_ПП_Темижбекский высокий берег', 'knv-bolot-albashi_lpk_orz-new_WGS84': 'Каневской_район_ ЛПК Болото Албаши', 'knv-chelbas-plavn_lpk_orz-new_WGS84': 'Каневской_район_ЛПК Челбасские плавни', 'knv-nijn-beisug-plavn_lpk_orz-new_WGS84': 'Каневской_район_ЛПК Нижнебейсугские плавни', 'kln-ponur-les_pp-orz-new_WGS84': 'Калининский_район_ПП Понурский лес', 'kln-ponura-liman_lpk_orz-new_WGS84': 'Калининский_район_ЛПК Понурский лиман', 'krl-balk-irinovka+_pp_orz-new_WGS84': 'Крыловский_район_ПП_Балка Ириновка', 'krl-balk-krutaya_pp_orz-new_WGS84': 'Крыловский район_Балка Крутая в долине реки Ея', 'krl-gryadin-plavn-rek-eya_pp_orz-new_WGS84': 'Крыловский_район_ПП_Грядинские плавни реки Ея', 'krl-balk-irinovka+_pp_orz-new_WGS84': 'Крыловский_район_ПП_Балка Ириновка', 'krm-uroch_kovalenkova_baga_pp_orz-new_WGS84': 'Крымский район_Коваленкова бага', 'ksh-poltavskiy-ostanec_pp_orz-new_WGS84': 'Кущевский район_Полтавский останец степи в долине реки Эльбузд', 'ksh-uroch-alekseevskoe_pp_orz-new_WGS84': 'Кущевский район_Урочище Алексеевское', 'ksh-shalfey-step-rek-elbuzd_pp_orz-new_WGS84': 'Кущевский_район_ПП_Шалфеевая_степь', 'mst-besleneev-pesheri_pp_orz-new_WGS84': 'Мостовской_р-н_ПП_Пещера Бесленеевская', 'nvk-kutanskaya-step_pp_orz-new_WGS84': 'Успенск_Новокуб_Армавир_р-н_ПП_Кутанская степь', 'nvp-fedorenk-bugr_pp_orz-new_WGS84': 'Новопокровский_район_ПП_Федоренковы бугры', 'nvk-fortshtadt-nagorn-step_pp_orz-new_WGS84': 'Армавир_Новокубанск_р-н_ПП_Фортштадская степь', 'nvr-abrau_zak+_orz-new_WGS84': 'Новороссийск_Абраусский заказник', 'nvr-abrau_zak_orz-IZMENENIE_WGS84': 'Новороссийск_Абраусский заказник_расширение Щурова', 'nvr-atakay-markoth_zak_orz-new_WGS84': 'Новороссийск_Атакайско-Маркотхский', 'nvr-kovilskaya-step_pp_orz-new_WGS84': 'Новороссийск_Ковыльская_степь_хребта_Сенетх', 'nvr-petrof-step-hr-seneth_pp_orz-new_WGS84': 'Новороссийск_Петрофитные_степи_Гора Самбурова', 'otr-dzheltmesskie-vysoty_zak_orz-new_WGS84': 'Отрадненский район_Джельтмесские Высоты', 'otr-chehrak+_zak_orz-new_WGS84': 'Отрадненский район_заказник Чехрак', 'otr-chehrak+_zak_orz-IZMENENIE_WGS84': 'Отрадненский район_заказник Чехрак_изменения ООПТ', 'pvl-urch-mugan_pp_orz-new_WGS84': 'Павловский_район_ПП_Урочище Муган', 'svr-hrebet-dlinn-gor_prz_orz-new_WGS84': 'Cеверский_район_ПРЗ Хребет Длинная гора', 'sch-aibga-grushevn_pp_orz-new_WGS84': 'Сочи_ПП_Аибгинский грушевник', 'sch-mamay-lesopark_zak_orz-new_WGS84': 'Сочи_Мамайский лесопарк', 'sch-urch-dzhegosh_pp_orz-new_WGS84': 'Сочи_ПП_Урочище Джегош', 'tmr-urochische-strelka-yuzhnaya_pp_orz-new_WGS84': 'Темрюкский район_Урочище Стрелка Южная', 'tbl-tbiliss_visokiy_bereg_pp_orz-new_WGS84': 'Тбилисский_р-н_ПП_Тбилисский высокий берег', 'tbl-savk-kruch_pp_orz-new_WGS84': 'Тбилисский_р-н_ПП_Савкины кручи', 'tps-gebeus-gorn-mass_zak_orz-new_WGS84': 'Туапсинский район_ПП_Гебеусский горный массив', 'usp-stepnoy_zak++_orz-new_WGS84': 'Успенский_р-н_заказник Степной', 'usp-uspen-poim-les_zak_orz-new_WGS84': 'Успенский_Отрадн_р-н_ПП_Успенские пойменные леса', 'ulb-uch-lesostep-rek-2-zelench_pp_orz-new_WGS84': 'Усть-Лаб_р-н_ПП_Участки лесостепи на р. Зеленчук', 'ulb-urochische-ladozhskoe_pp_orz-new_WGS84': 'Усть-Лабинский район_Урочище Ладожское', 'ulb-urochische-pyatihatskoe_pp_orz-new_WGS84': 'Усть-Лабинский район_Урочище Пятихатское', 'shr-eisk-plavn_lpk_orz-new_WGS84': 'Щербиновский район_ЛПК Ейские плавни', 'shr-uroch-tureckaya-krepost_ppk_orz-new_WGS84': 'Щербиновский район_ППК Турецкая крепость', }
Начало обработки¶
In [9]:
## Список файлов folder = 'Input_processing' # папка для обработки lst = shp_lst(folder)
['Input_processing\\tbl-tbiliss_visokiy_bereg_pp_orz-new_WGS84.shp', 'Input_processing\\Орнитологический Границы тропы.shp'] Всего: 2 файл(-а, -ов)
In [10]:
# Пакетная обработка for shp in lst: # Считываем файл gdf = gpd.read_file(shp) gdf = gdf.to_crs(7683) # Переводим в ГСК-2011 # Извлекаем имя файла shp_name = os.path.basename(shp).split('.')[0] # Счетчик для сохранения разных участков одной ООПТ counter = 0 # Превращаем возможных мултисоставной объект (multipart) в односоставной (singlepart) gdf = gdf.explode(index_parts=False).reset_index(drop=True) # Создает список фреймов из каждой объекта (если многосоставной) list_of_single_row_gdfs = [gdf.iloc[[i]] for i in range(len(gdf))] for el in list_of_single_row_gdfs: # Счетчик для номера участка внутри одного шейпа counter += 1 merge_lst = [] # Вызов функции для расчета и форматирования координат df = coordinates(el) name_xlsx = shp_name + '.xlsx' df.to_excel(name_xlsx, header=False, index=False) # Форматируем файл через функцию format_xlsx(name_xlsx, counter) merge_lst.append(name_xlsx) # Создаем шапку для таблицы (функция), если файл не существует if not os.path.exists('template_gc.xlsx'): template_gc('template_gc.xlsx') # Добавлеем шапку таблицы в список файлов для обработки под номером 0 merge_lst.insert(0, 'template_gc.xlsx') # Имя из словаря со списком район+ООПТ по имени shape-файла final_name = str('Координаты_') + dic_oopt[shp_name] + "_Участок_" + str(counter) + '.xlsx' # Объединяем все файлы из списка с сохранением исходного форматирования (функция) concat_xlsx_with_style(merge_lst, final_name) # Добавляем второй лист с параметрами преобразования координат для геологического сервиса add_cs(final_name)
C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:7: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.font = src_cell.font.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:8: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.border = src_cell.border.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:9: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.fill = src_cell.fill.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:11: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.protection = src_cell.protection.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:12: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.alignment = src_cell.alignment.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:7: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.font = src_cell.font.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:8: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.border = src_cell.border.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:9: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.fill = src_cell.fill.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:11: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.protection = src_cell.protection.copy() C:\Users\Evgenii\AppData\Local\Temp\ipykernel_43168\1470149036.py:12: DeprecationWarning: Call to deprecated function copy (Use copy(obj) or cell.obj = cell.obj + other). tgt_cell.alignment = src_cell.alignment.copy()
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Cell In[10], line 40 37 merge_lst.insert(0, 'template_gc.xlsx') 39 # Имя из словаря со списком район+ООПТ по имени shape-файла ---> 40 final_name = str('Координаты_') + dic_oopt[shp_name] + "_Участок_" + str(counter) + '.xlsx' 42 # Объединяем все файлы из списка с сохранением исходного форматирования (функция) 43 concat_xlsx_with_style(merge_lst, final_name) KeyError: 'Орнитологический Границы тропы'
4. Поворотные точки с 3 координатными системами (WGS84, МСК23, ГСК2011) - АНАПСКАЯ ПЕРЕСЫПЬ¶
In [56]:
import geopandas as gpd import pandas as pd from os.path import splitext import os
In [57]:
# Функция создания списка шейпфайлов для обработки def shp_lst(folder): lst = [] for file in os.listdir(folder): if file.endswith(".shp"): lst.append(os.path.join(folder, file)) print(lst[:2], '\n', f'Всего: {len(lst)} файл(-а, -ов)') return lst ### Функция создания таблицы с коодинатными системами # Извлекаем имя файла def coord_crs(shp): shp_name = os.path.splitext(os.path.basename(shp))[0] gdf = gpd.read_file(shp) # WGS84 gdf1 = gdf.to_crs(4326) # Извлекаем координаты вертексов полигона (формат X, Y в текущей координатной системе) df1 = gdf1.get_coordinates() # Добавляем индекс к таблице df1.reset_index(inplace = True, drop = True) # Первая строка с 1 df1.index += 1 df1 = df1.reset_index() df1 = round(df1,6) df1.columns = ['N', 'Dolgota_X_WGS84', 'Shirota_Y_WGS84', ] df1 = df1[['N', 'Shirota_Y_WGS84', 'Dolgota_X_WGS84']] #MSK23 gdf2 = gdf.to_crs(msk23_1) df2 = gdf2.get_coordinates() # Добавляем индекс к таблице df2.reset_index(inplace = True, drop = True) # Первая строка с 1 df2.index += 1 df2 = df2.reset_index() # Округляем df2 = round(df2) df2.columns = ['N', 'Dolgota_X_MSK23', 'Shirota_Y_MSK23', ] df2 = df2[['N', 'Shirota_Y_MSK23', 'Dolgota_X_MSK23']] #GSK2011 gdf3 = gdf.to_crs(7683) df3 = gdf3.get_coordinates() # Добавляем индекс к таблице df3.reset_index(inplace = True, drop = True) # Первая строка с 1 df3.index += 1 df3 = df3.reset_index() df3 = round(df3,6) df3.columns = ['N', 'Dolgota_X_GSK2011', 'Shirota_Y_GSK2011',] df3 = df3[['N', 'Shirota_Y_GSK2011','Dolgota_X_GSK2011']] # Объединение таблиц с координатами (df1 и df2) df1 = df1.merge(df2,left_on='N', right_on='N') df1 = df1.merge(df3,left_on='N', right_on='N') # Добавляем в конец дубликат первой строки df1 = df1.take(list(range(len(df1) - 1)) + [0]) # Файлы Excel помещаются в папку скрипта с именем исходном файла df1.to_excel(shp_name + '.xlsx', index=False)
In [58]:
# Считываем проекцию в формате WKT и сохраняем ее в переменной from pyproj import CRS with open(r'F:\PROJECT-Colormap\Преобразования географических проекций (СК, ГСК-2011, МСК)\МСК23 проекции Краснодар\MCK-23_Zone_1N.prj', 'r') as f: #wkt = f.read() msk23_1 = f.read() # crs = CRS.from_wkt(wkt) with open(r'F:\PROJECT-Colormap\Преобразования географических проекций (СК, ГСК-2011, МСК)\МСК23 проекции Краснодар\MCK-23_Zone_2N.prj', 'r') as f: msk23_2 = f.read()
In [59]:
## Список файлов folder = 'Input_processing' # папка для обработки lst = shp_lst(folder)
['Input_processing\\Орнитологический Границы тропы.shp'] Всего: 1 файл(-а, -ов)
In [60]:
# Обработка for shp in lst: coord_crs(shp)
Тоже, что и выше, но создают для каждого элемента в шейпе отдельный файл Excel и название файла по номеру кад. участка (делал через Perplexity)¶
In [55]:
import geopandas as gpd import pandas as pd from os.path import splitext, basename, join import os from pyproj import CRS # ---------- Настройки ---------- INPUT_FOLDER = 'Input_processing' # папка для обработки OUTPUT_FOLDER = 'Output_excel' # куда сохранять Excel CAD_FIELD = 'cad_number' # имя столбца с кадастровым номером MSK23_WKT_FILE = r'F:\PROJECT-Colormap\Преобразования географических проекций (СК, ГСК-2011, МСК)\МСК23 проекции Краснодар\MCK-23_Zone_1N.prj' # ---------- Вспомогательные ---------- def ensure_dir(path): os.makedirs(path, exist_ok=True) def shp_lst(folder): lst = [os.path.join(folder, f) for f in os.listdir(folder) if f.lower().endswith(".shp")] print(lst[:2], '\n', f'Всего: {len(lst)} файл(-а, -ов)') return lst def load_wkt(path): with open(path, 'r', encoding='utf-8') as f: return f.read() # ---------- Основная функция ---------- def export_features_to_excels(shp, msk23_wkt, output_dir=OUTPUT_FOLDER, cad_field=CAD_FIELD): ensure_dir(output_dir) shp_name = os.path.splitext(os.path.basename(shp))[0] gdf = gpd.read_file(shp) if cad_field not in gdf.columns: print(f'WARNING: {cad_field} не найден в {shp_name}. Будут использованы имена по индексу.') # Преобразования систем координат заранее gdf_wgs = gdf.to_crs(4326) # WGS84 gdf_msk = gdf.to_crs(msk23_wkt) # MSK23 WKT gdf_gsk = gdf.to_crs(7683) # GSK2011 / EPSG:7683 # Проходим по каждому объекту for i, (wgs_geom, msk_geom, gsk_geom, row) in enumerate(zip(gdf_wgs.geometry, gdf_msk.geometry, gdf_gsk.geometry, gdf.itertuples(index=False))): # Имя файла base_name = getattr(row, cad_field) if cad_field in gdf.columns else f'{shp_name}_feat_{i+1}' base_name = str(base_name).replace(':', '_') # Собираем координаты вершин полигона/мультиполигона def coords_as_df(geom, x_name, y_name, round_ndigits=None, round0=False): # Разворачиваем в список колец def extract_rings(g): if g is None or g.is_empty: return [] if g.geom_type == 'Polygon': return [g.exterior] + list(g.interiors) if g.geom_type == 'MultiPolygon': rings = [] for p in g.geoms: rings.append(p.exterior) rings.extend(list(p.interiors)) return rings # Для LineString/Point fallback return [g] pts = [] for ring in extract_rings(geom): for x, y in ring.coords: pts.append((y, x)) # хотим сначала Y, потом X df = pd.DataFrame(pts, columns=[y_name, x_name]).reset_index(drop=True) df.index += 1 df.insert(0, 'N', df.index) if round0: df[[y_name, x_name]] = df[[y_name, x_name]].round(0) elif round_ndigits is not None: df[[y_name, x_name]] = df[[y_name, x_name]].round(round_ndigits) # Добавляем дубликат первой строки в конец if len(df) > 0: first_row = df.iloc[[0]].copy() df = pd.concat([df.iloc[:-1], first_row], ignore_index=True) df.index += 1 df['N'] = df.index return df df_wgs = coords_as_df(wgs_geom, 'Dolgota_X_WGS84', 'Shirota_Y_WGS84', round_ndigits=6) df_msk = coords_as_df(msk_geom, 'Dolgota_X_MSK23', 'Shirota_Y_MSK23', round0=True) df_gsk = coords_as_df(gsk_geom, 'Dolgota_X_GSK2011', 'Shirota_Y_GSK2011', round_ndigits=6) # Объединяем по N df = df_wgs.merge(df_msk, on='N', how='outer').merge(df_gsk, on='N', how='outer') # Сохраняем в Excel out_path = os.path.join(output_dir, f'{base_name}.xlsx') df.to_excel(out_path, index=False) print(f'Готово: {shp_name} -> {output_dir}') # ---------- Инициализация ---------- msk23_wkt = load_wkt(MSK23_WKT_FILE) # ---------- Запуск ---------- for shp in shp_lst(INPUT_FOLDER): export_features_to_excels(shp, msk23_wkt)
['Input_processing\\selected_cad_numbers.shp'] Всего: 1 файл(-а, -ов) Готово: selected_cad_numbers -> Output_excel
5. Преобразование в MapInfo¶
In [ ]:
import geopandas as gpd import pandas as pd from os.path import splitext import os # Функция создания списка шейпфайлов для обработки def shp_lst(folder): lst = [] for file in os.listdir(folder): if file.endswith(".shp"): lst.append(os.path.join(folder, file)) print(lst[:2], '\n', f'Всего: {len(lst)} файл(-а, -ов)') return lst
In [48]:
### Перевод в MapInfo folder = 'Output' for shp in lst: shp_name = os.path.splitext(os.path.basename(shp))[0] gdf = gpd.read_file(shp) gdf.to_file(folder + '\\' + shp_name, driver='MapInfo File')