pax_global_header00006660000000000000000000000064151033175170014515gustar00rootroot0000000000000052 comment=ceeefd4aa1a2dd2244d76b172114c5ddcda7fa74 gdal-grass-2.0.0/000077500000000000000000000000001510331751700135405ustar00rootroot00000000000000gdal-grass-2.0.0/.clang-format000066400000000000000000000037701510331751700161220ustar00rootroot00000000000000--- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: true AlignConsecutiveAssignments: false AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Allman BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH] IndentCaseLabels: true IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 1000000000 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: false SortIncludes: false SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 4 UseTab: Never --- Language: Json BasedOnStyle: llvm gdal-grass-2.0.0/.git-blame-ignore-revs000066400000000000000000000000511510331751700176340ustar00rootroot000000000000008e7529a0ba0735ff6bab9e8e67439ad22238bcf3 gdal-grass-2.0.0/.github/000077500000000000000000000000001510331751700151005ustar00rootroot00000000000000gdal-grass-2.0.0/.github/how_to_release.md000066400000000000000000000014121510331751700204170ustar00rootroot00000000000000# How to release gdal-grass 1. Release tag must be created on CLI (letting GitHub create the tag in connection with with GH online publish will **not** create and archive the release files): ```bash git switch main git pull git status # Set version, do not forget! version="x.y.z" # Assuming 'upstream' points to the OSGeo fork commit=$(git rev-parse HEAD) tag_message="gdal-grass ${version}" git tag -a -m "${tag_message}" $version $commit git push --tags upstream ``` 2. Create a new release on GH, based on the above created tag. "Publish release" will activate the *publish.yml* workflow, which will create and add the packages to the release. 3. Transfer the packaged files to . gdal-grass-2.0.0/.github/workflows/000077500000000000000000000000001510331751700171355ustar00rootroot00000000000000gdal-grass-2.0.0/.github/workflows/apt.txt000066400000000000000000000000721510331751700204610ustar00rootroot00000000000000gdal-bin grass grass-dev libgdal-dev libpq-dev pkg-config gdal-grass-2.0.0/.github/workflows/publish.yml000066400000000000000000000063701510331751700213340ustar00rootroot00000000000000--- name: Create or check a release draft on: push: branches: - main tags: - '**' pull_request: branches: - main paths: - .github/** - utils/** env: OUT_DIR: ${{ github.workspace }}/../g_outdir GDAL-GRASS: gdal-grass-${{ github.ref_name }} permissions: contents: write jobs: build-n-publish: name: Package and create release draft runs-on: ubuntu-latest steps: - name: Check out repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ github.ref }} fetch-depth: 0 - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: '3.11' - name: Install Python dependencies run: | python -m pip install --upgrade pip python -m pip install PyYAML - name: Create output directory run: | mkdir ${{ env.OUT_DIR }} - name: Generate ChangeLog file run: | lasttag="" for tag in $(git tag -n --sort=-creatordate | cut -d' ' -f 1); do if [[ ${{ github.ref_name }} != *"rc"* && $tag != *"rc"* && \ $tag != ${{ github.ref_name }} ]] then lasttag="$tag" break elif [[ $tag != ${{ github.ref_name }} && ${{ github.ref_name }} == *"rc"* ]] then lasttag="$tag" break fi done echo "Creating CHANGELOG since v$lasttag" echo "$(python ./utils/generate_release_notes.py log main \ $lasttag ${{ github.ref_name }})" >> ${{ env.OUT_DIR }}/CHANGELOG - name: Create tarballs (for tags only) if: startsWith(github.ref, 'refs/tags/') run: | cd .. tar -cvf ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar --exclude=".g*" \ --transform s/gdal-grass/${{ env.GDAL-GRASS }}/ gdal-grass cd ${{ env.OUT_DIR }} gzip -9k ${{ env.GDAL-GRASS }}.tar md5sum ${{ env.GDAL-GRASS }}.tar.gz > ${{ env.GDAL-GRASS }}.tar.gz.md5 sha256sum ${{ env.GDAL-GRASS }}.tar.gz > ${{ env.GDAL-GRASS }}.tar.gz.sha256 xz -9e ${{ env.GDAL-GRASS }}.tar md5sum ${{ env.GDAL-GRASS }}.tar.xz > ${{ env.GDAL-GRASS }}.tar.xz.md5 sha256sum ${{ env.GDAL-GRASS }}.tar.xz > ${{ env.GDAL-GRASS }}.tar.xz.sha256 - name: Publish draft release to GitHub (for tags only) if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: name: GDAL-GRASS GIS driver ${{ github.ref_name }} body_path: ${{ env.OUT_DIR }}/CHANGELOG draft: true prerelease: ${{ contains(github.ref, 'RC') }} files: | ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar.gz ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar.gz.md5 ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar.gz.sha256 ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar.xz ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar.xz.md5 ${{ env.OUT_DIR }}/${{ env.GDAL-GRASS }}.tar.xz.sha256 gdal-grass-2.0.0/.github/workflows/test_simple.sh000077500000000000000000000010701510331751700220220ustar00rootroot00000000000000#!/usr/bin/env bash # fail on non-zero return code from a subprocess set -e # print commands set -x if [ -z "$1" ]; then echo "Usage: $0 GDAL_AUTOLOAD_DIR" exit 1 fi # non-existent variables as an errors set -u export GDAL_DRIVER_PATH=$1 # Using LD_LIBRARY_PATH workaround for GRASS GIS < 7.8.8 export LD_LIBRARY_PATH=$(grass --config path)/lib # test GRASS GIS raster map gdalinfo $HOME/grassdata/nc_spm_08_micro/PERMANENT/cellhd/elevation # test GRASS GIS vector map ogrinfo -so -al $HOME/grassdata/nc_spm_08_micro/PERMANENT/vector/firestations/head gdal-grass-2.0.0/.github/workflows/ubuntu.yml000066400000000000000000000036431510331751700212100ustar00rootroot00000000000000--- name: Build on Ubuntu on: - push - pull_request env: GDAL_AUTOLOAD_CMAKE_DIR: $HOME/gdalplugins_cmake jobs: build: runs-on: ubuntu-latest steps: - name: Check out repository code uses: actions/checkout@v5 - name: Get dependencies run: | sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable sudo apt-get update -y sudo apt-get install -y wget git gawk findutils xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends \ --no-install-suggests - name: Setup test data run: | mkdir -p $HOME/grassdata cd $HOME/grassdata/ wget -c --quiet https://grass.osgeo.org/sampledata/north_carolina/nc_spm_08_micro.zip unzip nc_spm_08_micro.zip rm -f nc_spm_08_micro.zip - name: Create GDAL_AUTOLOAD directory for CMake run: | mkdir ${{ env.GDAL_AUTOLOAD_CMAKE_DIR }} - name: Set up Python for tests uses: actions/setup-python@v6 with: python-version: 3.11 - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install pytest pytest-env pytest-sugar gdal==3.6.4 - name: CMake configure run: | cmake -B build -DAUTOLOAD_DIR=${{ env.GDAL_AUTOLOAD_CMAKE_DIR }} - name: CMake build run: | cmake --build build - name: CTest run: | export GDAL_DRIVER_PATH=${GITHUB_WORKSPACE}/build export LD_LIBRARY_PATH=$(grass --config path)/lib cd build ctest --output-on-failure - name: Install with CMake run: | cmake --install build - name: Test executing of GDAL with driver on GRASS maps (CMake) run: | .github/workflows/test_simple.sh ${{ env.GDAL_AUTOLOAD_CMAKE_DIR }} gdal-grass-2.0.0/.gitignore000066400000000000000000000001741510331751700155320ustar00rootroot00000000000000*~ *.log *.o *.so *.dylib # CMake files (CMakeSettings.json is generated by Visual Studio) /build CMakeSettings.json *.pyc gdal-grass-2.0.0/.pre-commit-config.yaml000066400000000000000000000010451510331751700200210ustar00rootroot00000000000000--- repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.44.0 hooks: - id: markdownlint-fix - repo: https://github.com/pre-commit/mirrors-clang-format rev: v15.0.7 hooks: - id: clang-format types_or: [c, c++, javascript, json, objective-c] - repo: https://github.com/adrienverge/yamllint.git rev: v1.35.1 hooks: - id: yamllint gdal-grass-2.0.0/.yamllint000066400000000000000000000003521510331751700153720ustar00rootroot00000000000000--- extends: default rules: comments: level: error min-spaces-from-content: 1 document-start: level: error line-length: level: error max: 120 allow-non-breakable-inline-mappings: true truthy: disable gdal-grass-2.0.0/CMakeLists.txt000066400000000000000000000066111510331751700163040ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # ############################################################################## # CMake settings cmake_minimum_required(VERSION 3.10.0) set(CMAKE_COLOR_MAKEFILE ON) # set path to additional CMake modules set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) # specify the C++ standard set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ############################################################################## # Project and version set(CPACK_PACKAGE_VERSION_MAJOR "2") set(CPACK_PACKAGE_VERSION_MINOR "0") set(CPACK_PACKAGE_VERSION_PATCH "0") set(VERSION_SUFFIX "") set(COMPLETE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH} ) set(RELEASE_NAME ${COMPLETE_VERSION}${VERSION_SUFFIX}) project(gdal-grass VERSION ${COMPLETE_VERSION}) message(STATUS "gdal-grass version ${RELEASE_NAME}") include(CTest) # ############################################################################## # Dependencies and flags find_package(GDAL REQUIRED) find_package(GRASS REQUIRED) find_package(PostgreSQL) find_package(PROJ) if(NOT AUTOLOAD_DIR) execute_process( COMMAND ${GDAL_CONFIG} --prefix OUTPUT_VARIABLE GDALPREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) set(AUTOLOAD_DIR ${GDALPREFIX}/lib/gdalplugins) if(NOT AUTOLOAD_DIR) message(FATAL_ERROR "Could not set GDAL plugin path") endif() endif() message( STATUS "Using ${AUTOLOAD_DIR} as GDAL shared library autoload directory") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_CPL -DGRASS_GISBASE=\\\"${GRASS_GISBASE}\\\"") # ############################################################################## # Build set(GLIB_SOURCES source/grass.cpp) set(OLIB_SOURCES source/ogrgrassdriver.cpp source/ogrgrassdatasource.cpp source/ogrgrasslayer.cpp source/ogrgrass.h) add_library(gdal_grass SHARED ${GLIB_SOURCES}) set_target_properties(gdal_grass PROPERTIES PREFIX "") set_target_properties(gdal_grass PROPERTIES OUTPUT_NAME "gdal_GRASS") set_target_properties(gdal_grass PROPERTIES INSTALL_RPATH "${GRASS_GISBASE}/lib") target_include_directories( gdal_grass PRIVATE ${CMAKE_SOURCE_DIR} ${GDAL_INCLUDE_DIR} ${PostgreSQL_INCLUDE_DIRS} ${GRASS_INCLUDE} ${PROJ_INCLUDE_DIRS}) target_link_libraries(gdal_grass PUBLIC ${GDAL_LIBRARY} ${G_LIBS}) install(TARGETS gdal_grass DESTINATION ${AUTOLOAD_DIR}) add_library(ogr_grass SHARED ${OLIB_SOURCES}) set_target_properties(ogr_grass PROPERTIES PREFIX "") set_target_properties(ogr_grass PROPERTIES OUTPUT_NAME "ogr_GRASS") set_target_properties(ogr_grass PROPERTIES INSTALL_RPATH "${GRASS_GISBASE}/lib") target_include_directories( ogr_grass PRIVATE ${CMAKE_SOURCE_DIR} ${GDAL_INCLUDE_DIR} ${PostgreSQL_INCLUDE_DIRS} ${GRASS_INCLUDE} ${PROJ_INCLUDE_DIRS}) target_link_libraries(ogr_grass PUBLIC ${GDAL_LIBRARY} ${G_LIBS}) install(TARGETS ogr_grass DESTINATION ${AUTOLOAD_DIR}) # ############################################################################## # Tests if (Python_LOOKUP_VERSION) set(Python_FIND_STRATEGY VERSION) find_package(Python ${Python_LOOKUP_VERSION} EXACT COMPONENTS Interpreter Development NumPy) else () set(Python_FIND_STRATEGY LOCATION) find_package(Python 3.11 COMPONENTS Interpreter Development NumPy) endif () if (BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/autotest") add_subdirectory(autotest) endif () gdal-grass-2.0.0/LICENSE000066400000000000000000000022301510331751700145420ustar00rootroot00000000000000GDAL-GRASS GIS Driver Licensing =============================== GDAL-GRASS GIS Driver is licensed under an MIT style license with the following terms: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gdal-grass-2.0.0/README.md000066400000000000000000000070601510331751700150220ustar00rootroot00000000000000# Introduction Description: GRASS GIS extension for the GDAL library GDAL is a translator library for raster geospatial data formats. As a library, it presents a single abstract data model to the calling application for all supported formats. This extension provides access to GRASS data via GDAL. This package contains the two standalone GDAL-GRASS GIS drivers, the [raster driver](docs/grass_raster.md) and the [vector driver](docs/grass_vector.md) for [GRASS GIS](http://grass.osgeo.org/) raster and vector file support in [GDAL](https://gdal.org/). This approach avoids circular dependencies with GRASS depending on GDAL, but GDAL with GRASS support depending on GRASS. With this driver package you configure and install GDAL normally, then build and install GRASS normally and finally build and install this driver in [GDAL's "autoload" directory](https://gdal.org/user/configoptions.html#driver-management). ## Installation To build this driver it is necessary for it to find GDAL and GRASS support files. **Building with CMake:** ```bash cd gdal-grass mkdir build && cd build cmake .. cmake --build . cmake --install . ``` CMake usually works out the location of GDAL and GRASS, but at times you may need/wish to set some configurations manually, e.g.: ```bash cmake .. -DAUTOLOAD_DIR=/opt/local/lib/gdalplugins \ -DGDAL_CONFIG_PREFER_PATH=/opt/local/bin \ -DGRASS_BIN_PREFER_PATH=/opt/local/bin ``` ## Usage Set the driver path (e.g. in $HOME/.bashrc): ```bash GDAL_DRIVER_PATH="/usr/lib/gdalplugins" ``` Access GRASS GIS raster data from GDAL: ```bash gdalinfo $HOME/grassdata/nc_spm_08_grass7/PERMANENT/cellhd/elevation ``` Access GRASS GIS vector data from GDAL-OGR: ```bash ogrinfo -so -al $HOME/grassdata/nc_spm_08_grass7/PERMANENT/vector/zipcodes/head ``` ## Where is the gdal-grass driver available? - Linux: - Windows: - other operating systems: please add here ## Version number has been restarted Note that during the transit of this driver out of core GDAL we have decided to reset the version numbering back to 1. So: older packages show 3.x which the new driver is 1.x (or later). ## Release management List of [milestones](https://github.com/OSGeo/gdal-grass/milestones). ## Tracking upstream changes The release policies of the GDAL-GRASS driver are (so far) fairly simple: - we follow the GDAL development for their breaking changes: - as of 2022, any GDAL 2+ and GDAL 3+ version is compliant, with GDAL 3+ recommended. - we follow the GRASS GIS development for their breaking changes: - as of 2022, any GRASS GIS 7+ and GRASS GIS 8+ version is compliant, with GRASS GIS 8+ recommended. We expect low maintenance needs for this driver. ## Using milestones For easier planning, each issue and pull request will be assigned to a [milestone](https://github.com/OSGeo/gdal-grass/milestones). ## QA / CI Any pull request opened in this repository is compiled and tested with [GitHub Actions](https://github.com/OSGeo/gdal-grass/actions) against the GDAL version included in Ubuntu (see related [CI workflow](https://github.com/OSGeo/gdal-grass/blob/main/.github/workflows/ubuntu.yml)). Improvements and other workflows are welcome, ideally as a [pull request](https://github.com/OSGeo/gdal-grass/pulls). ## Found a bug? Please open an [issue](https://github.com/OSGeo/gdal-grass/issues) describing the problem along with a reproducible example. ## Who is involved here? Please see the list of [contributors](https://github.com/OSGeo/gdal-grass/graphs/contributors). gdal-grass-2.0.0/autotest/000077500000000000000000000000001510331751700154105ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/CMakeLists.txt000066400000000000000000000073251510331751700201570ustar00rootroot00000000000000# CMake4GDAL project is distributed under MIT license. See accompanying file LICENSE.txt. #[[ option(AUTOTEST_DOWNLOAD_TEST_DATA "Autotest to download test data" OFF) option(AUTOTEST_SLOW_TEST "Autotest to run slow test" OFF) if (NOT DEFINED ENV{CTEST_PARALLEL_LEVEL}) set(PARALLEL_OPTION "-j1") endif () ]] if (Python_Interpreter_FOUND) if (WIN32) # If running GDAL as a CustomBuild Command os MSBuild, "ERROR bla:" is considered as failing the job. This is rarely # the intended behavior list(APPEND PYTHON_RUN_ENV "CPL_ERROR_SEPARATOR=\\;") endif () # Set TEST_ENV that goes into pytest.ini # Set GDAL_DATA if(WIN32) file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/data" GDAL_DATA) else() set(GDAL_DATA "${PROJECT_BINARY_DIR}/data") endif() set(TEST_ENV_ GDAL_DATA=${GDAL_DATA}) if (GDAL_DOWNLOAD_TEST_DATA) list(APPEND TEST_ENV_ GDAL_DOWNLOAD_TEST_DATA=YES) else () list(APPEND TEST_ENV_ "#GDAL_DOWNLOAD_TEST_DATA=YES") endif () if (GDAL_SLOW_TESTS) list(APPEND TEST_ENV_ GDAL_RUN_SLOW_TESTS=YES) else () list(APPEND TEST_ENV_ "#GDAL_RUN_SLOW_TESTS=YES") endif () # Conda enable PROJ_NETWORK but this does interfere with some of our tests due to some unexpected grid being used list(APPEND TEST_ENV_ PROJ_NETWORK=OFF) list(APPEND TEST_ENV_ "GDAL_DRIVER_PATH=${PROJECT_BINARY_DIR}") string(REPLACE ";" "\n " TEST_ENV "${TEST_ENV_}") set(AUTOTEST_LOG_FILE "${CMAKE_CURRENT_BINARY_DIR}/autotest.log") set(PYTEST_INI_HEADER_MESSAGE "This file was generated from ${GDAL_CMAKE_TEMPLATE_PATH}/pytest.ini.in using ${CMAKE_CURRENT_LIST_FILE}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pytest.ini.in ${CMAKE_CURRENT_BINARY_DIR}/pytest.ini @ONLY) unset(PYTEST_INI_HEADER_MESSAGE) function (copy_file_or_dir source dest) if (IS_DIRECTORY ${source}) message(STATUS "Copying contents of ${source} to ${destination}") execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${source} ${destination}) else() message(STATUS "Copying ${source} to ${destination}") execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${source} ${destination}) endif() endfunction() function (symlink_or_copy source destination) file(CREATE_LINK ${source} ${destination} RESULT res SYMBOLIC) if (NOT res EQUAL 0) copy_file_or_dir(${source} ${destination}) endif () endfunction () symlink_or_copy(${CMAKE_CURRENT_SOURCE_DIR}/conftest.py ${CMAKE_CURRENT_BINARY_DIR}/conftest.py) if (NOT "${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") foreach (subdir IN ITEMS pymod) # proj_grids cpp/data) if (SKIP_COPYING_AUTOTEST_SUBDIRS) message(STATUS "Skipping copying ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}") else () symlink_or_copy(${CMAKE_CURRENT_SOURCE_DIR}/${subdir} ${CMAKE_CURRENT_BINARY_DIR}/${subdir}) endif () endforeach () endif() foreach (tgt IN ITEMS ogr gdrivers) if (NOT "${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") if (SKIP_COPYING_AUTOTEST_SUBDIRS) message(STATUS "Skipping copying ${CMAKE_CURRENT_SOURCE_DIR}/${tgt}") else () symlink_or_copy(${CMAKE_CURRENT_SOURCE_DIR}/${tgt} ${CMAKE_CURRENT_BINARY_DIR}/${tgt}) endif () endif() add_custom_target( autotest_${tgt} COMMAND ${CMAKE_COMMAND} -E env ${PYTHON_RUN_ENV} ${Python_EXECUTABLE} -m pytest -c ${CMAKE_CURRENT_BINARY_DIR}/pytest.ini ${tgt} DEPENDS ${GDAL_LIB_TARGET_NAME} gdalapps python_binding) add_test(NAME autotest_${tgt} COMMAND ${Python_EXECUTABLE} -m pytest -c ${CMAKE_CURRENT_BINARY_DIR}/pytest.ini ${tgt}) set_property(TEST autotest_${tgt} PROPERTY ENVIRONMENT "${PYTHON_RUN_ENV}") endforeach () endif () gdal-grass-2.0.0/autotest/README.md000066400000000000000000000014441510331751700166720ustar00rootroot00000000000000# GDAL-GRASS driver test suite ## How to run tests 1. You need to install `pytest` to run the test suite. This should do it: ```bash pip install --upgrade pip pip install pytest pytest-env pytest-sugar gdal==$(gdal-config --version) ``` 2. Configure ```bash cd gdal-grass cmake -B build cd build cmake build . ``` 3. Then, run tests with: ```bash ctest ``` or: ```bash cd autotest # i.e gdal-grass/build/autotest pytest ``` 4. Some quick usage tips: ```bash # get more verbose output; don't capture stdout/stdin pytest -vvs # run all the ogr tests pytest ogr # run a particular module only pytest ogr/ogr_grass.py # run a particular test case in a module pytest ogr/ogr_grass.py::test_ogr_grass_point2 ``` gdal-grass-2.0.0/autotest/conftest.py000077500000000000000000000241761510331751700176240ustar00rootroot00000000000000# coding: utf-8 from __future__ import absolute_import, division, print_function import os import sys import pytest from osgeo import gdal, ogr, osr # Explicitly enable exceptions since autotest/ now assumes them to be # enabled gdal.UseExceptions() # Put the pymod dir on the path, so modules can `import gdaltest` sys.path.insert(0, os.path.join(os.path.dirname(__file__), "pymod")) # put the autotest dir on the path too. This lets us import all test modules sys.path.insert(1, os.path.dirname(__file__)) # These files may be non-importable, and don't contain tests anyway. # So we skip searching them during test collection. collect_ignore = [ "kml_generate_test_files.py", "gdrivers/netcdf_cfchecks.py", "gdrivers/generate_bag.py", "gdrivers/generate_fits.py", ] collect_ignore_glob = ["pymod/*.py"] if "APPLY_LOCALE" in os.environ: import locale locale.setlocale(locale.LC_ALL, "") def setup_proj_search_paths(): from osgeo import osr proj_grids_path = os.path.join(os.path.dirname(__file__), "proj_grids") assert os.path.exists(proj_grids_path) proj_db_tmpdir = os.path.join( os.path.dirname(__file__), "gcore", "tmp", "proj_db_tmpdir" ) proj_db_tmpdir_filename = os.path.join(proj_db_tmpdir, "proj.db") src_proj_db_filename = None for path in osr.GetPROJSearchPaths(): if os.path.exists(os.path.join(path, "proj.db")): src_proj_db_filename = os.path.join(path, "proj.db") break if src_proj_db_filename is None: print("Cannot find source proj.db") sys.exit(1) if ( not os.path.exists(proj_db_tmpdir_filename) or os.stat(proj_db_tmpdir_filename).st_mtime < os.stat(src_proj_db_filename).st_mtime or os.stat(proj_db_tmpdir_filename).st_size != os.stat(src_proj_db_filename).st_size ): import shutil from filelock import FileLock # We need to do the copy of proj.db from its source directory to # gcore/tmp/proj_db_tmpdir under a lock to prevent pytest invocations # run concurrently to overwrite in parallel, leading to PROJ being # confused by the file being overwritten after opening, whereas PROJ # assumes it to be immutable. lock = FileLock(proj_db_tmpdir + ".lock") with lock: if ( not os.path.exists(proj_db_tmpdir_filename) or os.stat(proj_db_tmpdir_filename).st_mtime < os.stat(src_proj_db_filename).st_mtime or os.stat(proj_db_tmpdir_filename).st_size != os.stat(src_proj_db_filename).st_size ): print("Copying %s to %s" % (src_proj_db_filename, proj_db_tmpdir)) if not os.path.exists(proj_db_tmpdir): os.mkdir(proj_db_tmpdir, 0o755) shutil.copy(src_proj_db_filename, proj_db_tmpdir) assert os.path.exists(proj_db_tmpdir_filename) osr.SetPROJSearchPaths([proj_db_tmpdir, proj_grids_path]) # setup_proj_search_paths() @pytest.fixture(scope="module", autouse=True) def chdir_to_test_file(request): """ Changes to the same directory as the test file. Also puts that directory at the start of sys.path, so that imports of other files in the same directory are easy. Tests have grown to expect this. NOTE: This happens when the test is *run*, not during collection. So test modules must not rely on it at module level. """ old = os.getcwd() new_cwd = os.path.dirname(request.module.__file__) os.chdir(new_cwd) sys.path.insert(0, new_cwd) yield if sys.path and sys.path[0] == new_cwd: sys.path.pop(0) os.chdir(old) def pytest_collection_modifyitems(config, items): # skip test with @ptest.mark.require_run_on_demand when RUN_ON_DEMAND is not set skip_run_on_demand_not_set = pytest.mark.skip("RUN_ON_DEMAND not set") import gdaltest import ogrtest drivers_checked = {} # Note: when adding a new custom marker, document it in cmake/template/pytest.ini.in for item in items: for mark in item.iter_markers("require_driver"): driver_name = mark.args[0] if driver_name not in drivers_checked: driver = gdal.GetDriverByName(driver_name) or ogr.GetDriverByName( driver_name ) drivers_checked[driver_name] = bool(driver) if driver: # Store the driver on gdaltest module so test functions can assume it's there. setattr(gdaltest, "%s_drv" % driver_name.lower(), driver) if not drivers_checked[driver_name]: # skip tests with @pytest.mark.require_driver(name) when the driver isn't available item.add_marker(pytest.mark.skip(f"Driver {driver_name} not present")) if not gdal.GetConfigOption("RUN_ON_DEMAND"): for mark in item.iter_markers("require_run_on_demand"): item.add_marker(skip_run_on_demand_not_set) for mark in item.iter_markers("slow"): if not gdaltest.run_slow_tests(): item.add_marker(pytest.mark.skip("GDAL_RUN_SLOW_TESTS not set")) for mark in item.iter_markers("require_creation_option"): driver, option = mark.args drv = gdal.GetDriverByName(driver) if drv is None: item.add_marker( pytest.mark.skip(f"{driver} driver is not included in this build") ) elif option not in drv.GetMetadata()["DMD_CREATIONOPTIONLIST"]: item.add_marker( pytest.mark.skip( f"{driver} creation option {option} not supported in this build" ) ) for mark in item.iter_markers("require_geos"): if not ogrtest.have_geos(): item.add_marker(pytest.mark.skip("GEOS not available")) required_version = ( mark.args[0] if len(mark.args) > 0 else 0, mark.args[1] if len(mark.args) > 1 else 0, mark.args[2] if len(mark.args) > 2 else 0, ) actual_version = ( ogr.GetGEOSVersionMajor(), ogr.GetGEOSVersionMinor(), ogr.GetGEOSVersionMicro(), ) if actual_version < required_version: item.add_marker( pytest.mark.skip( f"Requires GEOS >= {'.'.join(str(x) for x in required_version)}" ) ) for mark in item.iter_markers("require_proj"): required_version = ( mark.args[0], mark.args[1] if len(mark.args) > 1 else 0, mark.args[2] if len(mark.args) > 2 else 0, ) actual_version = ( osr.GetPROJVersionMajor(), osr.GetPROJVersionMinor(), osr.GetPROJVersionMicro(), ) if actual_version < required_version: item.add_marker( pytest.mark.skip( f"Requires PROJ >= {'.'.join(str(x) for x in required_version)}" ) ) for mark in item.iter_markers("require_curl"): if not gdaltest.built_against_curl(): item.add_marker(pytest.mark.skip("curl support not available")) def pytest_addoption(parser): parser.addini("gdal_version", "GDAL version for which pytest.ini was generated") def pytest_configure(config): test_version = config.getini("gdal_version") lib_version = gdal.__version__ if not lib_version.startswith(test_version): raise Exception( f"Attempting to run tests for GDAL {test_version} but library version is " f"{lib_version}. Do you need to run setdevenv.sh ?" ) def list_loaded_dlls(): try: import psutil except ImportError: return None process = psutil.Process() loaded_dlls = [] for dll in process.memory_maps(): if os.path.exists(dll.path): loaded_dlls.append(dll.path) loaded_dlls = sorted(loaded_dlls) return "\n".join(loaded_dlls) def pytest_report_header(config): gdal_header_info = "GDAL Build Info:" for item in gdal.VersionInfo("BUILD_INFO").strip().split("\n"): gdal_header_info += "\n " + item.replace("=", ": ") import gdaltest gdal_download_test_data = gdal.GetConfigOption("GDAL_DOWNLOAD_TEST_DATA") if gdal_download_test_data is None: gdal_download_test_data = "undefined" gdal_header_info += f"\nGDAL_DOWNLOAD_TEST_DATA: {gdal_download_test_data}" if not gdaltest.download_test_data(): gdal_header_info += " (tests relying on downloaded data may be skipped)" gdal_run_slow_tests = gdal.GetConfigOption("GDAL_RUN_SLOW_TESTS") if gdal_run_slow_tests is None: gdal_run_slow_tests = "undefined" gdal_header_info += f"\nGDAL_RUN_SLOW_TESTS: {gdal_run_slow_tests}" if not gdaltest.run_slow_tests(): gdal_header_info += ' (tests marked as "slow" will be skipped)' if gdal.GetConfigOption("CI"): loaded_dlls = list_loaded_dlls() if loaded_dlls: gdal_header_info += "\nLoaded shared objects:\n" + loaded_dlls return gdal_header_info @pytest.fixture() def tmp_vsimem(request): import pathlib import re # sanitize test name using same method as pytest's tmp_path subdir = re.sub(r"[\W]", "_", request.node.name) # return a pathlib object so that behavior matches tmp_path # and we can easily switch between the two path = pathlib.PurePosixPath("/vsimem") / subdir gdal.Mkdir(str(path), 0o755) yield path gdal.RmdirRecursive(str(path)) # Fixture to run a test function with pytest_benchmark @pytest.fixture(scope="function") def decorate_with_benchmark(request, benchmark): def run_under_benchmark(f, benchmark): def test_with_benchmark_fixture(*args, **kwargs): @benchmark def do(): f(*args, **kwargs) return test_with_benchmark_fixture request.node.obj = run_under_benchmark(request.node.obj, benchmark) gdal-grass-2.0.0/autotest/gdrivers/000077500000000000000000000000001510331751700172355ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/000077500000000000000000000000001510331751700201465ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/000077500000000000000000000000001510331751700241625ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/PERMANENT/000077500000000000000000000000001510331751700255135ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/PERMANENT/PROJ_INFO000066400000000000000000000002441510331751700270230ustar00rootroot00000000000000name: Universe Transverse Mercator datum: nad83 towgs84: 0.000,0.000,0.000 proj: utm ellps: grs80 a: 6378137.0000000000 es: 0.0066943800 f: 298.2572221010 zone: 18 gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/PERMANENT/PROJ_UNITS000066400000000000000000000000461510331751700271720ustar00rootroot00000000000000unit: meter units: meters meters: 1.0 gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/README.txt000066400000000000000000000001531510331751700256570ustar00rootroot00000000000000This is a very small extract from the sample set https://grass.osgeo.org/sampledata/fire_grass6data.tar.gz gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/000077500000000000000000000000001510331751700263205ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/WIND000066400000000000000000000004751510331751700270120ustar00rootroot00000000000000proj: 1 zone: 18 north: 4391490 south: 4385100 east: 551890 west: 547000 cols: 245 rows: 320 e-w resol: 19.95918367 n-s resol: 19.96875 top: 1 bottom: 0 cols3: 245 rows3: 320 depths: 1 e-w resol3: 19.95918367 n-s resol3: 19.96875 t-b resol: 1 gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cell/000077500000000000000000000000001510331751700272375ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cell/elevation000066400000000000000000000310151510331751700311500ustar00rootroot00000000000000  #&),/258;>ADGTiI5vE  i ` E :  d M6}niF#l'fL}?h8g%P%LoL{)V?xR(a'V  < a !!E!n!!!" ")"H"g""""##/#R#o####$$#$B$_$~$$$%%%8%U%x%%&&"&C&f&&&&'%'H'q'''((8(]((((() )$)A)\)w))))***>*Q*d*y******+++.+G+`+++++,,',<,Q,b,q,,,,,,--+-B-Y-p------..*.G.b.}....//=/\/}///00$0C0b0000011121I1Z1o111111111111111111111112222 2 k w)<s)3q)+p)"n)l)  R)    O)      L)     I)  F)     C(    @(    =(  6(  -(      +(     ((     &(    #(    !(    (   (     (     (      (   (   '  '     '     ' " ' ' ' '    '   !'     "'     #'     $'    %'           &'          ''         )&    +&    -&      /&        2&        9&          ?&          B&          D&         G&     J&     M&       O&      R&      S&         S&      T%      U%      V%       V%       W%         X$     Y$     Y$     Z$     [$     \#     ]#        ]#        ^#          ^#         ^#         ^"        _"        _"        _"     _"     _!     `!      `!       `!       `!        `         a         a          a         a         a        b        b        b         b           b           b           c        c        c             c              c              d             d               d               d              d                 e                 e                  e                e                e              f               f               f            f          f          g         g          g          g         g          g          g               g             f             f                f              f              f            f              f              f       $  f       $   f       $   f      $   f            f            f        f       f       f          f        f        f         f         f         f     $  f     &  f     &  f     f       f       f        f      f!     f! !  f!   f"    d"     c"      b#      a#     `#  ^#  ]$   \$      [$      Z$    Z$      Z$      Z$      [$       [$       \$        \$       ]$       ]$         ]$        ^$        ^$      _$  _$  _$   _$   _$   _$   ^$   ^$   ^$   ^$   ^$   ^$  ^$    ^$    _$ +  _$-  _$-  _$ -  _$P  _$P  _$N  _$-  _$-  _%)  _%(   _%(   _%&   ^%%   ]%%   ]%    \%     \%     [% %    Z&&   Z&&   Z&7    Z&7   Z'6   Z'6*  ['6*  ['6*  [(5)  [(4*  [(4*  [(2    \)   \)   \)'   \) (   \* (   \*   )   ]*  '   ]*  '   ]+  $   ]+  #  ]+  #  ]+  ^+  ^+  ^+   ^* ^* ^*  _*  _)  _)     _)     _)     `)   `(  `(  `(  `(  `(  a)  a+  a,  a-   $a.  & a/  & b0  #b1    %b3    %b4   "b;    bB   cI  cN  dQ  fT   hW! jZ! l]$ n`% pe rj to vt wy ygdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cell_misc/000077500000000000000000000000001510331751700302525ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cell_misc/elevation/000077500000000000000000000000001510331751700322405ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cell_misc/elevation/range000066400000000000000000000000111510331751700332470ustar00rootroot000000000000000 0 3 27 gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cellhd/000077500000000000000000000000001510331751700275535ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/gdrivers/data/small_grass_dataset/demomapset/cellhd/elevation000066400000000000000000000003241510331751700314630ustar00rootroot00000000000000proj: 1 zone: 18 north: 4391490 south: 4385100 east: 551890 west: 547000 cols: 245 rows: 320 e-w resol: 19.95918367 n-s resol: 19.96875 format: 0 compressed: 1 gdal-grass-2.0.0/autotest/gdrivers/grass.py000077500000000000000000000042361510331751700207360ustar00rootroot00000000000000#!/usr/bin/env pytest ############################################################################### # # Project: GDAL/OGR Test Suite # Purpose: GRASS Testing. # Author: Even Rouault # ############################################################################### # Copyright (c) 2008, Even Rouault # # SPDX-License-Identifier: MIT # ############################################################################### from osgeo import gdal import gdaltest ############################################################################### # Test if GRASS driver is present def test_grass_load_driver(): grassdriver = gdal.GetDriverByName("GRASS") assert grassdriver is not None, "Failed to load GRASS driver" ############################################################################### # Read existing simple 1 band GRASS dataset. def test_grass_2(): tst = gdaltest.GDALTest( "GRASS", "small_grass_dataset/demomapset/cellhd/elevation", 1, 41487 ) srs = """PROJCS["UTM Zone 18, Northern Hemisphere", GEOGCS["grs80", DATUM["North_American_Datum_1983", SPHEROID["Geodetic_Reference_System_1980",6378137,298.257222101], TOWGS84[0.000,0.000,0.000]], PRIMEM["Greenwich",0], UNIT["degree",0.0174532925199433]], PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin",0], PARAMETER["central_meridian",-75], PARAMETER["scale_factor",0.9996], PARAMETER["false_easting",500000], PARAMETER["false_northing",0], UNIT["meter",1]]""" ret = tst.testOpen(check_prj=srs) return ret ############################################################################### # Read metadata from band 1 def test_grass_3(): ds = gdal.Open("./data/small_grass_dataset/demomapset/cellhd/elevation") assert ds is not None, "Cannot find layer" band = ds.GetRasterBand(1) assert band.GetNoDataValue() == 0.0 assert band.GetMinimum() == 3.0 assert band.GetMaximum() == 27.0 assert band.GetMetadataItem("COLOR_TABLE_RULES_COUNT") == "0" assert band.GetColorInterpretation() == 1 # GCI_GrayIndex gdal-grass-2.0.0/autotest/ogr/000077500000000000000000000000001510331751700161775ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/ogr/data/000077500000000000000000000000001510331751700171105ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/000077500000000000000000000000001510331751700204415ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/DEFAULT_WIND000066400000000000000000000004161510331751700222720ustar00rootroot00000000000000proj: 3 zone: 0 north: 90N south: 90S east: 180E west: 180W cols: 360 rows: 180 e-w resol: 1 n-s resol: 1 top: 1 bottom: 0 cols3: 360 rows3: 180 depths: 1 e-w resol3: 1 n-s resol3: 1 t-b resol: 1 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/MYNAME000066400000000000000000000000241510331751700213460ustar00rootroot00000000000000GRASS Demo Location gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/PROJ_EPSG000066400000000000000000000000131510331751700217460ustar00rootroot00000000000000epsg: 4326 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/PROJ_INFO000066400000000000000000000000611510331751700217460ustar00rootroot00000000000000name: Lat/Lon proj: ll datum: wgs84 ellps: wgs84 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/PROJ_SRID000066400000000000000000000000121510331751700217500ustar00rootroot00000000000000EPSG:4326 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/PROJ_UNITS000066400000000000000000000000501510331751700221130ustar00rootroot00000000000000unit: degree units: degrees meters: 1.0 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/VAR000066400000000000000000000001211510331751700210060ustar00rootroot00000000000000DB_DRIVER: sqlite DB_DATABASE: $GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/WIND000066400000000000000000000004561510331751700211320ustar00rootroot00000000000000proj: 3 zone: 0 north: 90N south: 90S east: 180E west: 180W cols: 360 rows: 180 e-w resol: 1 n-s resol: 1 top: 1.000000000000000 bottom: 0.000000000000000 cols3: 360 rows3: 180 depths: 1 e-w resol3: 1 n-s resol3: 1 t-b resol: 1 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/sqlite/000077500000000000000000000000001510331751700217425ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/sqlite/sqlite.db000066400000000000000000002700001510331751700235510ustar00rootroot00000000000000SQLite format 3@   .  |91indexcountry_boundaries_catcountry_boundariesCREATE UNIQUE INDEX country_boundaries_cat ON country_boundaries (cat )x11tablecountry_boundariescountry_boundariesCREATE TABLE country_boundaries(cat INTEGER, SOVEREIGNT TEXT, TYPE TEXT, ADMIN TEXT, ADM0_A3 TEXT, NAME TEXT, NAME_LONG TEXT, POP_EST DOUBLE PRECISION, GDP_MD_EST DOUBLE PRECISION, ECONOMY TEXT, INCOME_GRP TEXT, WIKIPEDIA DOUBLE PRECISION, FIPS_10 TEXT, ISO_A2 TEXT, ISO_A3 TEXT, CONTINENT TEXT, SUBREGION TEXT, REGION_WB TEXT) m V ? ( ycM7  s\D- Q X  t 9 MS?;q(!/!!!?')1MozambiqueSovereign countryMozambiqueMOZMozambiqueMozambiqueJI7. Least developed region5. Low incomeMZMOZAfricaEastern AfricaSub-Saharan AfricaGC/C?')1United Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africa0/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia /59%3MongoliaSovereign countryMongoliaMNGMongoliaMongolia.gv%6. Developing region4. Lower middle incomeMNMNGAsiaEastern AsiaEast Asia & Pacific+/59+7MacedoniaSovereign countryMacedoniaMKDMacedoniaMacedoniaI\6. Developing region3. Upper middle incomeMKMKDEuropeSouthern EuropeEurope & Central Asia#/59+7AlbaniaSovereign countryAlbaniaALBAlbaniaAlbania7U26. Developing region4. Lower middle incomeALALBEuropeSouthern EuropeEurope & Central Asia#/59+7AlbaniaSovereign countryAlbaniaALBAlbaniaAlbania7U26. Developing region4. Lower middle incomeALALBEuropeSouthern EuropeEurope & Central AsiaE///!/?;'1Equatorial GuineaSovereign countryEquatorial GuineaGNQEq. GuineaEquatorial Guinea 67. Least developed region2. High income: nonOECDGQGNQAfricaMiddle AfricaSub-Saharan Africa9'/'''?')1Guinea BissauSovereign countryGuinea BissauGNBGuinea-BissauGuinea-Bissauh @A7. Least developed region5. Low incomeGWGNBAfricaWestern AfricaSub-Saharan Africa/593FijiSovereign countryFijiFJIFijiFijijP 6. Developing region4. Lower middle incomeFJFJIOceaniaMelanesiaEast Asia & Pacific( !'!!!59)1 SomalilandIndeterminateSomalilandSOLSomalilandSomaliland5g/6. Developing region4. Lower middle income-99-99AfricaEastern AfricaSub-Saharan Africa! /?')1 EthiopiaSovereign countryEthiopiaETHEthiopiaEthiopiaZ 7. Least developed region5. Low incomeETETHAfricaEastern AfricaSub-Saharan Africa( !'!!!59)1 SomalilandIndeterminateSomalilandSOLSomalilandSomaliland5g/6. Developing region4. Lower middle income-99-99AfricaEastern AfricaSub-Saharan Africa /59+1 SudanSovereign countrySudanSDNSudanSudanlX6. Developing region4. Lower middle incomeSDSDNAfricaNorthern AfricaSub-Saharan Africa /?')1 EritreaSovereign countryEritreaERIEritreaEritreaV+@i7. Least developed region5. Low incomeERERIAfricaEastern AfricaSub-Saharan Africa/59+1SudanSovereign countrySudanSDNSudanSudanlX6. Developing region4. Lower middle incomeSDSDNAfricaNorthern AfricaSub-Saharan Africa/5;%7CyprusSovereign countryCyprusCYPCyprusCyprusX6. Developing region2. High income: nonOECDCYCYPAsiaWestern AsiaEurope & Central Asia0/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia=9%3ChinaCountryChinaCHNChinaChinaOɜy3. Emerging region: BRIC3. Upper middle incomeCNCHNAsiaEastern AsiaEast Asia & Pacific0/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia=9%3ChinaCountryChinaCHNChinaChinaOɜy3. Emerging region: BRIC3. Upper middle incomeCNCHNAsiaEastern AsiaEast Asia & Pacific0/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia, /A9)7BulgariaSovereign countryBulgariaBGRBulgariaBulgariamOn62. Developed region: nonG73. Upper middle incomeBGBGREuropeEastern EuropeEurope & Central Asia ~xrlf`ZTNHB<60*$ |vpjd^XRLF@:4.("  z t n h b \ V P J D > 8 2 , &     x p h ` X P H @ 8 0 (    x p h ` X P H @ 8 0 (    x p h ` X P H @ 8 0 (    x p h ` X P H @ 8 0 (    xph`XPH@80( xph`XPH@80( xph`XPH@80( xph`XPH@80( xph`XPH@80( ~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllkkjjiihhggffeeddccbbaa``__^^]]\\[[ZZYYXXWWVVUUTTSSRRQQPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@??>>==<<;;::99887766554433221100//..--,,++**))((''&&%%$$##""!!            ~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllkkjjiihhggffeeddccbbaa``__^^]]\\[[ZZYYXXWWVVUUTTSSRRQQPPOONNMMLLKKJJIIHHGGFFEEDDCCBBAA@@??>>==<<;;::99887766554433221100//..--,,++**))((''&&%%$$##""!!       6v  O %E\WN$-/59'1-CameroonSovereign countryCameroonCMRCameroonCameroon E6. Developing region4. Lower middle incomeCMCMRAfricaMiddle AfricaSub-Saharan Africa$,/59'1,CameroonSovereign countryCameroonCMRCameroonCameroon E6. Developing region4. Lower middle incomeCMCMRAfricaMiddle AfricaSub-Saharan Africa0+/1=9)7+RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia,*!/!!!59%7*KazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia,)!/!!!59%7)KazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia0(/1=9)7(RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia '/59%3'MongoliaSovereign countryMongoliaMNGMongoliaMongolia.gv%6. Developing region4. Lower middle incomeMNMNGAsiaEastern AsiaEast Asia & Pacific &/59%3&MongoliaSovereign countryMongoliaMNGMongoliaMongolia.gv%6. Developing region4. Lower middle incomeMNMNGAsiaEastern AsiaEast Asia & Pacific0%/1=9)7%RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia$=9%3$ChinaCountryChinaCHNChinaChinaOɜy3. Emerging region: BRIC3. Upper middle incomeCNCHNAsiaEastern AsiaEast Asia & Pacific#=9%3#ChinaCountryChinaCHNChinaChinaOɜy3. Emerging region: BRIC3. Upper middle incomeCNCHNAsiaEastern AsiaEast Asia & Pacific0"/59''?"SurinameSovereign countrySurinameSURSurinameSurinameW6. Developing region3. Upper middle incomeSRSURSouth AmericaSouth AmericaLatin America & Caribbean!;5)7!FranceCountryFranceFRAFranceFranceq x1. Developed region: G71. High income: OECDFRFRAEuropeWestern EuropeEurope & Central Asia0 /59''? SurinameSovereign countrySurinameSURSurinameSurinameW6. Developing region3. Upper middle incomeSRSURSouth AmericaSouth AmericaLatin America & Caribbean'/?93VanuatuSovereign countryVanuatuVUTVanuatuVanuatuU@7. Least developed region4. Lower middle incomeVUVUTOceaniaMelanesiaEast Asia & PacificJ=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaJ=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaJ=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaJ=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth America=+/+#+?93Solomon IslandsSovereign countrySolomon IslandsSLBSolomon Is.Solomon Islands 67. Least developed region4. Lower middle incomeSBSLBOceaniaMelanesiaEast Asia & Pacific=+/+#+?93Solomon IslandsSovereign countrySolomon IslandsSLBSolomon Is.Solomon Islands 67. Least developed region4. Lower middle incomeSBSLBOceaniaMelanesiaEast Asia & PacificGC/C?')1United Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africa 1  ] ] )/w9>,-!/!!!59%7AzerbaijanSovereign countryAzerbaijanAZEAzerbaijanAzerbaijan}P/*6. Developing region3. Upper middle incomeAZAZEAsiaWestern AsiaEurope & Central Asia#,/59)7MoldovaSovereign countryMoldovaMDAMoldovaMoldovaA)6. Developing region4. Lower middle incomeMDMDAEuropeEastern EuropeEurope & Central Asia&+/?9+1LesothoSovereign countryLesothoLSOLesothoLesotho 7. Least developed region4. Lower middle incomeLSLSOAfricaSouthern AfricaSub-Saharan Africa&*59%AIsraelDisputedPalestinePSXPalestinePalestine>+@Wb\(6. Developing region4. Lower middle incomePSPSEAsiaWestern AsiaMiddle East & North Africa)/?9'!BhutanSovereign countryBhutanBTNBhutanBhutan 7. Least developed region4. Lower middle incomeBTBTNAsiaSouthern AsiaSouth AsiaA()/)!)A5)7Czech RepublicSovereign countryCzech RepublicCZECzech Rep.Czech Republic@ 2. Developed region: nonG71. High income: OECDCZCZEEuropeEastern EuropeEurope & Central Asia['=/=5=?''1Central African RepublicSovereign countryCentral African RepublicCAFCentral African Rep.Central African RepublicD ~7. Least developed region5. Low incomeCFCAFAfricaMiddle AfricaSub-Saharan Africa4&/;9''?ParaguaySovereign countryParaguayPRYParaguayParaguayjp5. Emerging region: G204. Lower middle incomePYPRYSouth AmericaSouth AmericaLatin America & Caribbean%/;')1ZimbabweSovereign countryZimbabweZWEZimbabweZimbabweP$k5. Emerging region: G205. Low incomeZWZWEAfricaEastern AfricaSub-Saharan Africa-$!/!!!59%7UzbekistanSovereign countryUzbekistanUZBUzbekistanUzbekistan;6. Developing region4. Lower middle incomeUZUZBAsiaCentral AsiaEurope & Central Asia"#!/!!!5'%7TajikistanSovereign countryTajikistanTJKTajikistanTajikistanp#3h6. Developing region5. Low incomeTJTJKAsiaCentral AsiaEurope & Central Asia""!/!!!5'%7KyrgyzstanSovereign countryKyrgyzstanKGZKyrgyzstanKyrgyzstanR-Z6. Developing region5. Low incomeKGKGZAsiaCentral AsiaEurope & Central Asia !/?''!NepalSovereign countryNepalNPLNepalNepalױyh7. Least developed region5. Low incomeNPNPLAsiaSouthern AsiaSouth Asia /?')1MaliSovereign countryMaliMLIMaliMaliHk87. Least developed region5. Low incomeMLMLIAfricaWestern AfricaSub-Saharan Africa2/;9''?BoliviaSovereign countryBoliviaBOLBoliviaBolivia(5. Emerging region: G204. Lower middle incomeBOBOLSouth AmericaSouth AmericaLatin America & Caribbean1/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia%/59)7UkraineSovereign countryUkraineUKRUkraineUkraineU+/X6. Developing region4. Lower middle incomeUAUKREuropeEastern EuropeEurope & Central Asia1/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central AsiaK=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth America#/;5'-'CanadaSovereign countryCanadaCANCanadaCanadah 1. Developed region: G71. High income: OECDCACANNorth AmericaNorthern AmericaNorth AmericaK=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth America b_ N p  f *[Em b#/;5'-'CanadaSovereign countryCanadaCANCanadaCanadah 1. Developed region: G71. High income: OECDCACANNorth AmericaNorthern AmericaNorth America+/59'+?BelizeSovereign countryBelizeBLZBelizeBelize 6. Developing region4. Lower middle incomeBZBLZNorth AmericaCentral AmericaLatin America & Caribbean1/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia=9%3ChinaCountryChinaCHNChinaChinaOɜy3. Emerging region: BRIC3. Upper middle incomeCNCHNAsiaEastern AsiaEast Asia & Pacific;5)7FranceCountryFranceFRAFranceFranceq x1. Developed region: G71. High income: OECDFRFRAEuropeWestern EuropeEurope & Central Asia#/?9%AYemenSovereign countryYemenYEMYemenYemenk7. Least developed region4. Lower middle incomeYEYEMAsiaWestern AsiaMiddle East & North Africa(/?93VanuatuSovereign countryVanuatuVUTVanuatuVanuatuU@7. Least developed region4. Lower middle incomeVUVUTOceaniaMelanesiaEast Asia & Pacific(/;913VietnamSovereign countryVietnamVNMVietnamVietnam/$5. Emerging region: G204. Lower middle incomeVNVNMAsiaSouth-Eastern AsiaEast Asia & Pacific:/;9''?VenezuelaSovereign countryVenezuelaVENVenezuelaVenezuela){t5. Emerging region: G203. Upper middle incomeVEVENSouth AmericaSouth AmericaLatin America & CaribbeanK=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaK=='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaK =='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaK =='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth AmericaK =='';5'-'United States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth America1 /;9''?UruguaySovereign countryUruguayURYUruguayUruguay5Q5. Emerging region: G203. Upper middle incomeUYURYSouth AmericaSouth AmericaLatin America & Caribbean" /A;%3TaiwanSovereign countryTaiwanTWNTaiwanTaiwan^ @2. Developed region: nonG72. High income: nonOECDTWTWNAsiaEastern AsiaEast Asia & Pacific+/59+ATunisiaSovereign countryTunisiaTUNTunisiaTunisiaC?.6. Developing region3. Upper middle incomeTNTUNAfricaNorthern AfricaMiddle East & North AfricaZ3/3335;'?Trinidad and TobagoSovereign countryTrinidad and TobagoTTOTrinidad and TobagoTrinidad and Tobago0qR6. Developing region2. High income: nonOECDTTTTONorth AmericaCaribbeanLatin America & Caribbean3%/%%%59%7TurkmenistanSovereign countryTurkmenistanTKMTurkmenistanTurkmenistanJtT6. Developing region3. Upper middle incomeTMTKMAsiaCentral AsiaEurope & Central Asia,/;913ThailandSovereign countryThailandTHAThailandThailandZH5. Emerging region: G203. Upper middle incomeTHTHAAsiaSouth-Eastern AsiaEast Asia & Pacific&/A5+7SwedenSovereign countrySwedenSWESwedenSweden=C@2. Developed region: nonG71. High income: OECDSESWEEuropeNorthern EuropeEurope & Central Asia/?')1SomaliaSovereign countrySomaliaSOMSomaliaSomaliaQ7. Least developed region5. Low incomeSOSOMAfricaEastern AfricaSub-Saharan Africa ;v ' u V7c*h0%/%%%?')1Sierra LeoneSovereign countrySierra LeoneSLESierra LeoneSierra LeonebDu7. Least developed region5. Low incomeSLSLEAfricaWestern AfricaSub-Saharan Africa>+/+#+?93Solomon IslandsSovereign countrySolomon IslandsSLBSolomon Is.Solomon Islands 67. Least developed region4. Lower middle incomeSBSLBOceaniaMelanesiaEast Asia & Pacific>+/+#+?93Solomon IslandsSovereign countrySolomon IslandsSLBSolomon Is.Solomon Islands 67. Least developed region4. Lower middle incomeSBSLBOceaniaMelanesiaEast Asia & Pacific>+/+#+?93Solomon IslandsSovereign countrySolomon IslandsSLBSolomon Is.Solomon Islands 67. Least developed region4. Lower middle incomeSBSLBOceaniaMelanesiaEast Asia & Pacific1~/1=9)7~RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1}/1=9)7}RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1|/1=9)7|RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1{/1=9)7{RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1z/1=9)7zRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1y/1=9)7yRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1x/1=9)7xRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1w/1=9)7wRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1v/1=9)7vRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1u/1=9)7uRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia1t/1=9)7tRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia.s/A5+7sPortugalSovereign countryPortugalPRTPortugalPortugalc.2. Developed region: nonG71. High income: OECDPTPRTEuropeSouthern EuropeEurope & Central AsiaAr=!###5;'?rUnited States of AmericaDependencyPuerto RicoPRIPuerto RicoPuerto Rico<V6. Developing region2. High income: nonOECDPRPRINorth AmericaCaribbeanLatin America & CaribbeanAq-/---593qPapua New GuineaSovereign countryPapua New GuineaPNGPapua New GuineaPapua New Guinea\m/36. Developing region4. Lower middle incomePGPNGOceaniaMelanesiaEast Asia & PacificAp-/---593pPapua New GuineaSovereign countryPapua New GuineaPNGPapua New GuineaPapua New Guinea\m/36. Developing region4. Lower middle incomePGPNGOceaniaMelanesiaEast Asia & PacificAo-/---593oPapua New GuineaSovereign countryPapua New GuineaPNGPapua New GuineaPapua New Guinea\m/36. Developing region4. Lower middle incomePGPNGOceaniaMelanesiaEast Asia & PacificAn-/---593nPapua New GuineaSovereign countryPapua New GuineaPNGPapua New GuineaPapua New Guinea\m/36. Developing region4. Lower middle incomePGPNGOceaniaMelanesiaEast Asia & Pacific =S U (   r Qa-q=8m#/###;913mPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific8l#/###;913lPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific8k#/###;913kPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific8j#/###;913jPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific8i#/###;913iPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific8h#/###;913hPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific8g#/###;913gPhilippinesSovereign countryPhilippinesPHLPhilippinesPhilippines<5. Emerging region: G204. Lower middle incomePHPHLAsiaSouth-Eastern AsiaEast Asia & Pacific,f/59'+?fPanamaSovereign countryPanamaPANPanamaPanama3F6. Developing region3. Upper middle incomePAPANNorth AmericaCentral AmericaLatin America & Caribbeane/;9'!ePakistanSovereign countryPakistanPAKPakistanPakistan A$5. Emerging region: G204. Lower middle incomePKPAKAsiaSouthern AsiaSouth Asiad/5;%AdOmanSovereign countryOmanOMNOmanOman4'6. Developing region2. High income: nonOECDOMOMNAsiaWestern AsiaMiddle East & North Africa8c####A5?3cNew ZealandCountryNew ZealandNZLNew ZealandNew Zealand@J2. Developed region: nonG71. High income: OECDNZNZLOceaniaAustralia and New ZealandEast Asia & Pacific8b####A5?3bNew ZealandCountryNew ZealandNZLNew ZealandNew Zealand@J2. Developed region: nonG71. High income: OECDNZNZLOceaniaAustralia and New ZealandEast Asia & Pacific%a/A5+7aNorwaySovereign countryNorwayNORNorwayNorwayGZ72. Developed region: nonG71. High income: OECDNONOREuropeNorthern EuropeEurope & Central Asia%`/A5+7`NorwaySovereign countryNorwayNORNorwayNorwayGZ72. Developed region: nonG71. High income: OECDNONOREuropeNorthern EuropeEurope & Central Asia%_/A5+7_NorwaySovereign countryNorwayNORNorwayNorwayGZ72. Developed region: nonG71. High income: OECDNONOREuropeNorthern EuropeEurope & Central Asia7^/59'+?^NicaraguaSovereign countryNicaraguaNICNicaraguaNicaraguaYA6. Developing region4. Lower middle incomeNINICNorth AmericaCentral AmericaLatin America & Caribbean%]/;9)1]NigeriaSovereign countryNigeriaNGANigeriaNigeria"(5. Emerging region: G204. Lower middle incomeNGNGAAfricaWestern AfricaSub-Saharan Africa\/?''1\ChadSovereign countryChadTCDChadChadx=7. Least developed region5. Low incomeTDTCDAfricaMiddle AfricaSub-Saharan Africa[/?')1[NigerSovereign countryNigerNERNigerNiger '87. Least developed region5. Low incomeNENERAfricaWestern AfricaSub-Saharan Africa(Z!'''5;3ZFranceDependencyNew CaledoniaNCLNew CaledoniaNew Caledoniaxl V6. Developing region2. High income: nonOECDNCNCLOceaniaMelanesiaEast Asia & Pacific!Y/59+1YNamibiaSovereign countryNamibiaNAMNamibiaNamibia ,36. Developing region3. Upper middle incomeNANAMAfricaSouthern AfricaSub-Saharan Africa)X/5913XMalaysiaSovereign countryMalaysiaMYSMalaysiaMalaysiadk,6. Developing region3. Upper middle incomeMYMYSAsiaSouth-Eastern AsiaEast Asia & Pacific)W/5913WMalaysiaSovereign countryMalaysiaMYSMalaysiaMalaysiadk,6. Developing region3. Upper middle incomeMYMYSAsiaSouth-Eastern AsiaEast Asia & Pacific ~d M 2 47C23~1V/1=9)7VRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia!U/59%3UMongoliaSovereign countryMongoliaMNGMongoliaMongolia.gv%6. Developing region4. Lower middle incomeMNMNGAsiaEastern AsiaEast Asia & Pacific1T/1=9)7TRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia!S/59%3SMongoliaSovereign countryMongoliaMNGMongoliaMongolia.gv%6. Developing region4. Lower middle incomeMNMNGAsiaEastern AsiaEast Asia & Pacific1R/=9'+?RMexicoSovereign countryMexicoMEXMexicoMexico x4. Emerging region: MIKT3. Upper middle incomeMXMEXNorth AmericaCentral AmericaLatin America & Caribbean)Q!/!!!?')1QMadagascarSovereign countryMadagascarMDGMadagascarMadagascar;%N7. Least developed region5. Low incomeMGMDGAfricaEastern AfricaSub-Saharan Africa+P/59+APMoroccoSovereign countryMoroccoMARMoroccoMoroccod6. Developing region4. Lower middle incomeMAMARAfricaNorthern AfricaMiddle East & North Africa'O/A9+7OLatviaSovereign countryLatviaLVALatviaLatvia" 2. Developed region: nonG73. Upper middle incomeLVLVAEuropeNorthern EuropeEurope & Central AsiaN/59'!NSri LankaSovereign countrySri LankaLKASri LankaSri LankaEcf6. Developing region4. Lower middle incomeLKLKAAsiaSouthern AsiaSouth Asia"M/59+AMLibyaSovereign countryLibyaLBYLibyaLibya`J"Z6. Developing region3. Upper middle incomeLYLBYAfricaNorthern AfricaMiddle East & North AfricaL/?')1LLiberiaSovereign countryLiberiaLBRLiberiaLiberia4~7. Least developed region5. Low incomeLRLBRAfricaWestern AfricaSub-Saharan Africa1K#/#/=5%3KSouth KoreaSovereign countrySouth KoreaKORKoreaRepublic of Korea0,^4. Emerging region: MIKT1. High income: OECDKRKORAsiaEastern AsiaEast Asia & Pacific$J/?'13JCambodiaSovereign countryCambodiaKHMCambodiaCambodia*Um$7. Least developed region5. Low incomeKHKHMAsiaSouth-Eastern AsiaEast Asia & PacificI/;')1IKenyaSovereign countryKenyaKENKenyaKenyaS"F5. Emerging region: G205. Low incomeKEKENAfricaEastern AfricaSub-Saharan Africa1H/1=9)7HRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia-G!/!!!59%7GKazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia1F/1=9)7FRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia-E!/!!!59%7EKazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia1D/1=9)7DRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia-C!/!!!59%7CKazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia1B/1=9)7BRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia-A!/!!!59%7AKazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia@/;5%3@JapanSovereign countryJapanJPNJapanJapanB(1. Developed region: G71. High income: OECDJPJPNAsiaEastern AsiaEast Asia & Pacific gM I * u V7>Lg?/;5%3?JapanSovereign countryJapanJPNJapanJapanB(1. Developed region: G71. High income: OECDJPJPNAsiaEastern AsiaEast Asia & Pacific>/;5%3>JapanSovereign countryJapanJPNJapanJapanB(1. Developed region: G71. High income: OECDJPJPNAsiaEastern AsiaEast Asia & Pacific)=/59'?=JamaicaSovereign countryJamaicaJAMJamaicaJamaica+Q6. Developing region3. Upper middle incomeJMJAMNorth AmericaCaribbeanLatin America & Caribbean</;5+7P7. Least developed region5. Low incomeCDCODAfricaMiddle AfricaSub-Saharan AfricaHRC/C?')1United Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africa)Q!/!!!?')1MozambiqueSovereign countryMozambiqueMOZMozambiqueMozambiqueJI7. Least developed region5. Low incomeMZMOZAfricaEastern AfricaSub-Saharan Africa)P!/!!!?')1MozambiqueSovereign countryMozambiqueMOZMozambiqueMozambiqueJI7. Least developed region5. Low incomeMZMOZAfricaEastern AfricaSub-Saharan AfricaZO9/9-959+7Bosnia and HerzegovinaSovereign countryBosnia and HerzegovinaBIHBosnia and Herz.Bosnia and HerzegovinaFe&t6. Developing region3. Upper middle incomeBABIHEuropeSouthern EuropeEurope & Central Asia!N/=9%7TurkeySovereign countryTurkeyTURTurkeyTurkey ,4. Emerging region: MIKT3. Upper middle incomeTRTURAsiaWestern AsiaEurope & Central Asia K @ 7 "kZEV0M!/!!!59+7MontenegroSovereign countryMontenegroMNEMontenegroMontenegro A6. Developing region3. Upper middle incomeMEMNEEuropeSouthern EuropeEurope & Central Asia,L/A;+7CroatiaSovereign countryCroatiaHRVCroatiaCroatiaDA2. Developed region: nonG72. High income: nonOECDHRHRVEuropeSouthern EuropeEurope & Central AsiaK/?')1UgandaSovereign countryUgandaUGAUgandaUganda7. Least developed region5. Low incomeUGUGAAfricaEastern AfricaSub-Saharan AfricaoJM/M+M?''1Democratic Republic of the CongoSovereign countryDemocratic Republic of the CongoCODDem. Rep. CongoDemocratic Republic of the Congo*>P7. Least developed region5. Low incomeCDCODAfricaMiddle AfricaSub-Saharan Africa*I#/##?')1South SudanSovereign countrySouth SudanSDSS. SudanSouth Sudan 37. Least developed region5. Low incomeSSSSDAfricaEastern AfricaSub-Saharan Africa,H!/!!!59%7AzerbaijanSovereign countryAzerbaijanAZEAzerbaijanAzerbaijan}P/*6. Developing region3. Upper middle incomeAZAZEAsiaWestern AsiaEurope & Central Asia1G/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia,F!/!!!59%7AzerbaijanSovereign countryAzerbaijanAZEAzerbaijanAzerbaijan}P/*6. Developing region3. Upper middle incomeAZAZEAsiaWestern AsiaEurope & Central Asia0E!/!!!59+7MontenegroSovereign countryMontenegroMNEMontenegroMontenegro A6. Developing region3. Upper middle incomeMEMNEEuropeSouthern EuropeEurope & Central Asia$D/59+7AlbaniaSovereign countryAlbaniaALBAlbaniaAlbania7U26. Developing region4. Lower middle incomeALALBEuropeSouthern EuropeEurope & Central Asia1C/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia3B#/#++?'%3North KoreaSovereign countryNorth KoreaPRKDem. Rep. KoreaDem. Rep. KoreaY؁@7. Least developed region5. Low incomeKPPRKAsiaEastern AsiaEast Asia & Pacific3A#/#++?'%3North KoreaSovereign countryNorth KoreaPRKDem. Rep. KoreaDem. Rep. KoreaY؁@7. Least developed region5. Low incomeKPPRKAsiaEastern AsiaEast Asia & Pacific1@/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia%?/A5+7NorwaySovereign countryNorwayNORNorwayNorwayGZ72. Developed region: nonG71. High income: OECDNONOREuropeNorthern EuropeEurope & Central Asia%>/A5+7NorwaySovereign countryNorwayNORNorwayNorwayGZ72. Developed region: nonG71. High income: OECDNONOREuropeNorthern EuropeEurope & Central Asia/=####A5)7NetherlandsCountryNetherlandsNLDNetherlandsNetherlands A2. Developed region: nonG71. High income: OECDNLNLDEuropeWestern EuropeEurope & Central Asia)</A5)7BelgiumSovereign countryBelgiumBELBelgiumBelgium2. Developed region: nonG71. High income: OECDBEBELEuropeWestern EuropeEurope & Central Asia/;####A5)7NetherlandsCountryNetherlandsNLDNetherlandsNetherlands A2. Developed region: nonG71. High income: OECDNLNLDEuropeWestern EuropeEurope & Central Asia1:/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia9A5+7FinlandCountryFinlandFINFinlandFinlandP2. Developed region: nonG71. High income: OECDFIFINEuropeNorthern EuropeEurope & Central Asia18/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia W O J OYZqt(7/A5+7EstoniaSovereign countryEstoniaESTEstoniaEstoniaӫk2. Developed region: nonG71. High income: OECDEEESTEuropeNorthern EuropeEurope & Central Asia%6/A5%AIsraelSovereign countryIsraelISRIsraelIsraeln`2. Developed region: nonG71. High income: OECDILISRAsiaWestern AsiaMiddle East & North Africa&5/;9+AEgyptSovereign countryEgyptEGYEgyptEgyptu45. Emerging region: G204. Lower middle incomeEGEGYAfricaNorthern AfricaMiddle East & North Africa&4/;9+AEgyptSovereign countryEgyptEGYEgyptEgyptu45. Emerging region: G204. Lower middle incomeEGEGYAfricaNorthern AfricaMiddle East & North Africa#3/;5'-'CanadaSovereign countryCanadaCANCanadaCanadah 1. Developed region: G71. High income: OECDCACANNorth AmericaNorthern AmericaNorth America2/?')1RwandaSovereign countryRwandaRWARwandaRwandaB%7. Least developed region5. Low incomeRWRWAAfricaEastern AfricaSub-Saharan Africa!1/=9%7TurkeySovereign countryTurkeyTURTurkeyTurkey ,4. Emerging region: MIKT3. Upper middle incomeTRTURAsiaWestern AsiaEurope & Central Asia0/;5+7ItalySovereign countryItalyITAItalyItalyv1. Developed region: G71. High income: OECDITITAEuropeSouthern EuropeEurope & Central Asia,//A;+7CroatiaSovereign countryCroatiaHRVCroatiaCroatiaDA2. Developed region: nonG72. High income: nonOECDHRHRVEuropeSouthern EuropeEurope & Central Asia(./A5+7EstoniaSovereign countryEstoniaESTEstoniaEstoniaӫk2. Developed region: nonG71. High income: OECDEEESTEuropeNorthern EuropeEurope & Central Asia&-/;9''?PeruSovereign countryPeruPERPeruPeru5. Emerging region: G203. Upper middle incomePEPERSouth AmericaSouth AmericaLatin America & Caribbean*,/;9''?ChileSovereign countryChileCHLChileChileRk5. Emerging region: G203. Upper middle incomeCLCHLSouth AmericaSouth AmericaLatin America & Caribbean+/5;%AOmanSovereign countryOmanOMNOmanOman4'6. Developing region2. High income: nonOECDOMOMNAsiaWestern AsiaMiddle East & North Africa!*/?'13MyanmarSovereign countryMyanmarMMRMyanmarMyanmarކ Z7. Least developed region5. Low incomeMMMMRAsiaSouth-Eastern AsiaEast Asia & Pacific")/?913LaosSovereign countryLaosLAOLao PDRLao PDRhJ67. Least developed region4. Lower middle incomeLALAOAsiaSouth-Eastern AsiaEast Asia & Pacific,(/59+7MacedoniaSovereign countryMacedoniaMKDMacedoniaMacedoniaI\6. Developing region3. Upper middle incomeMKMKDEuropeSouthern EuropeEurope & Central Asia&'/A5+7GreeceSovereign countryGreeceGRCGreeceGreece;2. Developed region: nonG71. High income: OECDGRGRCEuropeSouthern EuropeEurope & Central Asia4&!/!!!A5)7LuxembourgSovereign countryLuxembourgLUXLuxembourgLuxembourg2. Developed region: nonG71. High income: OECDLULUXEuropeWestern EuropeEurope & Central Asia%A5+7DenmarkCountryDenmarkDNKDenmarkDenmarkS^P2. Developed region: nonG71. High income: OECDDKDNKEuropeNorthern EuropeEurope & Central Asia8$#/###A5)7SwitzerlandSovereign countrySwitzerlandCHESwitzerlandSwitzerlandt2. Developed region: nonG71. High income: OECDCHCHEEuropeWestern EuropeEurope & Central Asia"##/###?''!AfghanistanSovereign countryAfghanistanAFGAfghanistanAfghanistanYV7. Least developed region5. Low incomeAFAFGAsiaSouthern AsiaSouth Asia""/?9)1ZambiaSovereign countryZambiaZMBZambiaZambiaD\7. Least developed region4. Lower middle incomeZMZMBAfricaEastern AfricaSub-Saharan Africa%!/59+1BotswanaSovereign countryBotswanaBWABotswanaBotswana`i6. Developing region3. Upper middle incomeBWBWAAfricaSouthern AfricaSub-Saharan Africa kQ F L P89/)k: %/%%%;9+1South AfricaSovereign countrySouth AfricaZAFSouth AfricaSouth Africa{I}5. Emerging region: G203. Upper middle incomeZAZAFAfricaSouthern AfricaSub-Saharan Africa+/A9)7RomaniaSovereign countryRomaniaROURomaniaRomaniaR$(2. Developed region: nonG73. Upper middle incomeROROUEuropeEastern EuropeEurope & Central Asia1%/%%%?')1Burkina FasoSovereign countryBurkina FasoBFABurkina FasoBurkina FasoDE7. Least developed region5. Low incomeBFBFAAfricaWestern AfricaSub-Saharan Africa/59%ASyriaSovereign countrySyriaSYRSyriaSyria356. Developing region4. Lower middle incomeSYSYRAsiaWestern AsiaMiddle East & North Africa)/59+1SwazilandSovereign countrySwazilandSWZSwazilandSwaziland&IF6. Developing region4. Lower middle incomeSZSWZAfricaSouthern AfricaSub-Saharan Africa)!/!!!?')1MozambiqueSovereign countryMozambiqueMOZMozambiqueMozambiqueJI7. Least developed region5. Low incomeMZMOZAfricaEastern AfricaSub-Saharan Africa,/A5)7SlovakiaSovereign countrySlovakiaSVKSlovakiaSlovakiaS\2. Developed region: nonG71. High income: OECDSKSVKEuropeEastern EuropeEurope & Central Asia/5;%AQatarSovereign countryQatarQATQatarQatar d6. Developing region2. High income: nonOECDQAQATAsiaWestern AsiaMiddle East & North Africa%/A5)7PolandSovereign countryPolandPOLPolandPolandK3 02. Developed region: nonG71. High income: OECDPLPOLEuropeEastern EuropeEurope & Central Asia0!/!!!59+7MontenegroSovereign countryMontenegroMNEMontenegroMontenegro A6. Developing region3. Upper middle incomeMEMNEEuropeSouthern EuropeEurope & Central Asia%/A5%AIsraelSovereign countryIsraelISRIsraelIsraeln`2. Developed region: nonG71. High income: OECDILISRAsiaWestern AsiaMiddle East & North Africa1/=913IndonesiaSovereign countryIndonesiaIDNIndonesiaIndonesiaR@ 4. Emerging region: MIKT4. Lower middle incomeIDIDNAsiaSouth-Eastern AsiaEast Asia & Pacific6!/!##?913East TimorSovereign countryEast TimorTLSTimor-LesteTimor-LesteD\ 7. Least developed region4. Lower middle incomeTLTLSAsiaSouth-Eastern AsiaEast Asia & Pacific)/A5)7HungarySovereign countryHungaryHUNHungaryHungary%2. Developed region: nonG71. High income: OECDHUHUNEuropeEastern EuropeEurope & Central Asia(/A5)7AustriaSovereign countryAustriaAUTAustriaAustria}Gi2. Developed region: nonG71. High income: OECDATAUTEuropeWestern EuropeEurope & Central Asia/59%7GeorgiaSovereign countryGeorgiaGEOGeorgiaGeorgiaFnT6. Developing region4. Lower middle incomeGEGEOAsiaWestern AsiaEurope & Central Asia/59%7ArmeniaSovereign countryArmeniaARMArmeniaArmenia-EIR6. Developing region4. Lower middle incomeAMARMAsiaWestern AsiaEurope & Central Asia&/;5)7GermanySovereign countryGermanyDEUGermanyGermany@,p1. Developed region: G71. High income: OECDDEDEUEuropeWestern EuropeEurope & Central Asia)/A5)7BelgiumSovereign countryBelgiumBELBelgiumBelgium2. Developed region: nonG71. High income: OECDBEBELEuropeWestern EuropeEurope & Central Asia: +/++59%7Northern CyprusSovereign countryNorthern CyprusCYNN. CyprusNorthern Cyprus 6. Developing region3. Upper middle income-99-99AsiaWestern AsiaEurope & Central Asia /?')1BurundiSovereign countryBurundiBDIBurundiBurundi% 7. Least developed region5. Low incomeBIBDIAfricaEastern AfricaSub-Saharan Africa( !/!!!?')1MauritaniaSovereign countryMauritaniaMRTMauritaniaMauritania/7. Least developed region5. Low incomeMRMRTAfricaWestern AfricaSub-Saharan Africa+ /59+AAlgeriaSovereign countryAlgeriaDZAAlgeriaAlgeria 6. Developing region3. Upper middle incomeDZDZAAfricaNorthern AfricaMiddle East & North Africa M 8 ' 6 y7pF" /?9)1ZambiaSovereign countryZambiaZMBZambiaZambiaD\7. Least developed region4. Lower middle incomeZMZMBAfricaEastern AfricaSub-Saharan AfricaHC/C?')1United Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africa%/59)7UkraineSovereign countryUkraineUKRUkraineUkraineU+/X6. Developing region4. Lower middle incomeUAUKREuropeEastern EuropeEurope & Central Asia1/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia%/59)7UkraineSovereign countryUkraineUKRUkraineUkraineU+/X6. Developing region4. Lower middle incomeUAUKREuropeEastern EuropeEurope & Central Asia1/1=9)7RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central AsiaHC/C?')1United Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africa/?')1MalawiSovereign countryMalawiMWIMalawiMalawiٹ'."7. Least developed region5. Low incomeMWMWIAfricaEastern AfricaSub-Saharan Africa/?')1MalawiSovereign countryMalawiMWIMalawiMalawiٹ'."7. Least developed region5. Low incomeMWMWIAfricaEastern AfricaSub-Saharan Africa!/=9%7TurkeySovereign countryTurkeyTURTurkeyTurkey ,4. Emerging region: MIKT3. Upper middle incomeTRTURAsiaWestern AsiaEurope & Central Asia/;9'AIranSovereign countryIranIRNIranIrand 5. Emerging region: G203. Upper middle incomeIRIRNAsiaSouthern AsiaMiddle East & North Africa,~/A5+7~SloveniaSovereign countrySloveniaSVNSloveniaSlovenia2. Developed region: nonG71. High income: OECDSISVNEuropeSouthern EuropeEurope & Central Asia?}#/###59'+?}El SalvadorSovereign countryEl SalvadorSLVEl SalvadorEl SalvadormBn6. Developing region4. Lower middle incomeSVSLVNorth AmericaCentral AmericaLatin America & Caribbean)|#/##?')1|South SudanSovereign countrySouth SudanSDSS. SudanSouth Sudan 37. Least developed region5. Low incomeSSSSDAfricaEastern AfricaSub-Saharan African{M/M+M?''1{Democratic Republic of the CongoSovereign countryDemocratic Republic of the CongoCODDem. Rep. CongoDemocratic Republic of the Congo*>P7. Least developed region5. Low incomeCDCODAfricaMiddle AfricaSub-Saharan Africa4z)'))?'+AzWestern SaharaIndeterminateWestern SaharaSAHW. SaharaWestern Sahara7. Least developed region5. Low incomeEHESHAfricaNorthern AfricaMiddle East & North Africa$y/59)7yBelarusSovereign countryBelarusBLRBelarusBelarus96. Developing region3. Upper middle incomeBYBLREuropeEastern EuropeEurope & Central Asia0x/1=9)7xRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia,w!/!!!59%7wKazakhstanSovereign countryKazakhstanKAZKazakhstanKazakhstan 6. Developing region3. Upper middle incomeKZKAZAsiaCentral AsiaEurope & Central Asia8v1/159+7vRepublic of SerbiaSovereign countryRepublic of SerbiaSRBSerbiaSerbiap96. Developing region3. Upper middle incomeRSSRBEuropeSouthern EuropeEurope & Central Asia(u/A5)7uHungarySovereign countryHungaryHUNHungaryHungary%2. Developed region: nonG71. High income: OECDHUHUNEuropeEastern EuropeEurope & Central Asia0t/1=9)7tRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia 6K O  c b:iR6s/59%7sGeorgiaSovereign countryGeorgiaGEOGeorgiaGeorgiaFnT6. Developing region4. Lower middle incomeGEGEOAsiaWestern AsiaEurope & Central Asia:r////59'1rRepublic of CongoSovereign countryRepublic of CongoCOGCongoRepublic of Congo=; ;6. Developing region4. Lower middle incomeCGCOGAfricaMiddle AfricaSub-Saharan AfricanqM/M+M?''1qDemocratic Republic of the CongoSovereign countryDemocratic Republic of the CongoCODDem. Rep. CongoDemocratic Republic of the Congo*>P7. Least developed region5. Low incomeCDCODAfricaMiddle AfricaSub-Saharan AfricaJp=='';5'-'pUnited States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth America"o/;5'-'oCanadaSovereign countryCanadaCANCanadaCanadah 1. Developed region: G71. High income: OECDCACANNorth AmericaNorthern AmericaNorth AmericaJn=='';5'-'nUnited States of AmericaCountryUnited States of AmericaUSAUnited StatesUnited StatesP1. Developed region: G71. High income: OECDUSUSANorth AmericaNorthern AmericaNorth America"m/;5'-'mCanadaSovereign countryCanadaCANCanadaCanadah 1. Developed region: G71. High income: OECDCACANNorth AmericaNorthern AmericaNorth Americal/?')1lTogoSovereign countryTogoTGOTogoTogo[%7. Least developed region5. Low incomeTGTGOAfricaWestern AfricaSub-Saharan Africak/?')1kNigerSovereign countryNigerNERNigerNiger '87. Least developed region5. Low incomeNENERAfricaWestern AfricaSub-Saharan Africa%j/?9)1jSenegalSovereign countrySenegalSENSenegalSenegal8U7. Least developed region4. Lower middle incomeSNSENAfricaWestern AfricaSub-Saharan Africa@i%/%%%A;%AiSaudi ArabiaSovereign countrySaudi ArabiaSAUSaudi ArabiaSaudi Arabia)2. Developed region: nonG72. High income: nonOECDSASAUAsiaWestern AsiaMiddle East & North AfricaGhC/C?')1hUnited Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africag/?')1gRwandaSovereign countryRwandaRWARwandaRwandaB%7. Least developed region5. Low incomeRWRWAAfricaEastern AfricaSub-Saharan Africa$f/59)7fUkraineSovereign countryUkraineUKRUkraineUkraineU+/X6. Developing region4. Lower middle incomeUAUKREuropeEastern EuropeEurope & Central Asia0e/1=9)7eRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia$d/59)7dUkraineSovereign countryUkraineUKRUkraineUkraineU+/X6. Developing region4. Lower middle incomeUAUKREuropeEastern EuropeEurope & Central Asia0c/1=9)7cRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia$b/59)7bUkraineSovereign countryUkraineUKRUkraineUkraineU+/X6. Developing region4. Lower middle incomeUAUKREuropeEastern EuropeEurope & Central Asiaa/?''1aChadSovereign countryChadTCDChadChadx=7. Least developed region5. Low incomeTDTCDAfricaMiddle AfricaSub-Saharan Africa`/?')1`NigerSovereign countryNigerNERNigerNiger '87. Least developed region5. Low incomeNENERAfricaWestern AfricaSub-Saharan AfricaG_C/C?')1_United Republic of TanzaniaSovereign countryUnited Republic of TanzaniaTZATanzaniaTanzaniarY7. Least developed region5. Low incomeTZTZAAfricaEastern AfricaSub-Saharan Africa^/?')1^MalawiSovereign countryMalawiMWIMalawiMalawiٹ'."7. Least developed region5. Low incomeMWMWIAfricaEastern AfricaSub-Saharan Africa2]/A9+7]LithuaniaSovereign countryLithuaniaLTULithuaniaLithuania6?kb2. Developed region: nonG73. Upper middle incomeLTLTUEuropeNorthern EuropeEurope & Central Asia N\ | . { ?  e` <N$\/59%A\LebanonSovereign countryLebanonLBNLebanonLebanon=K6. Developing region3. Upper middle incomeLBLBNAsiaWestern AsiaMiddle East & North Africa![/5;%A[KuwaitSovereign countryKuwaitKWTKuwaitKuwait)VFl6. Developing region2. High income: nonOECDKWKWTAsiaWestern AsiaMiddle East & North Africa Z/59+7ZKosovoSovereign countryKosovoKOSKosovoKosovo&6. Developing region4. Lower middle income-99-99EuropeSouthern EuropeEurope & Central Asia#Y/59+7YAlbaniaSovereign countryAlbaniaALBAlbaniaAlbania7U26. Developing region4. Lower middle incomeALALBEuropeSouthern EuropeEurope & Central AsiaX/59%AXJordanSovereign countryJordanJORJordanJordan`${z6. Developing region3. Upper middle incomeJOJORAsiaWestern AsiaMiddle East & North AfricaW/59%AWIraqSovereign countryIraqIRQIraqIraq6. Developing region4. Lower middle incomeIQIRQAsiaWestern AsiaMiddle East & North AfricaV/;9'AVIranSovereign countryIranIRNIranIrand 5. Emerging region: G203. Upper middle incomeIRIRNAsiaSouthern AsiaMiddle East & North AfricaU/;9'AUIranSovereign countryIranIRNIranIrand 5. Emerging region: G203. Upper middle incomeIRIRNAsiaSouthern AsiaMiddle East & North AfricaT/59%ATIraqSovereign countryIraqIRQIraqIraq6. Developing region4. Lower middle incomeIQIRQAsiaWestern AsiaMiddle East & North Africa8S1/159+7SRepublic of SerbiaSovereign countryRepublic of SerbiaSRBSerbiaSerbiap96. Developing region3. Upper middle incomeRSSRBEuropeSouthern EuropeEurope & Central Asia+R/A;+7RCroatiaSovereign countryCroatiaHRVCroatiaCroatiaDA2. Developed region: nonG72. High income: nonOECDHRHRVEuropeSouthern EuropeEurope & Central Asia8Q1/159+7QRepublic of SerbiaSovereign countryRepublic of SerbiaSRBSerbiaSerbiap96. Developing region3. Upper middle incomeRSSRBEuropeSouthern EuropeEurope & Central Asia+P/A;+7PCroatiaSovereign countryCroatiaHRVCroatiaCroatiaDA2. Developed region: nonG72. High income: nonOECDHRHRVEuropeSouthern EuropeEurope & Central Asia3O/59'+?OHondurasSovereign countryHondurasHNDHondurasHondurasv6. Developing region4. Lower middle incomeHNHNDNorth AmericaCentral AmericaLatin America & Caribbean8N/59'+?NGuatemalaSovereign countryGuatemalaGTMGuatemalaGuatemalaʕe 6. Developing region4. Lower middle incomeGTGTMNorth AmericaCentral AmericaLatin America & CaribbeanM/!?')1MGambiaSovereign countryGambiaGMBGambiaThe Gambia4m7. Least developed region5. Low incomeGMGMBAfricaWestern AfricaSub-Saharan AfricaL/?')1LEritreaSovereign countryEritreaERIEritreaEritreaV+@i7. Least developed region5. Low incomeERERIAfricaEastern AfricaSub-Saharan Africa0K/1=9)7KRussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia'J/A5+7JEstoniaSovereign countryEstoniaESTEstoniaEstoniaӫk2. Developed region: nonG71. High income: OECDEEESTEuropeNorthern EuropeEurope & Central Asia!I/?')1IEthiopiaSovereign countryEthiopiaETHEthiopiaEthiopiaZ 7. Least developed region5. Low incomeETETHAfricaEastern AfricaSub-Saharan AfricaH/?')1HEritreaSovereign countryEritreaERIEritreaEritreaV+@i7. Least developed region5. Low incomeERERIAfricaEastern AfricaSub-Saharan Africa!G/?')1GEthiopiaSovereign countryEthiopiaETHEthiopiaEthiopiaZ 7. Least developed region5. Low incomeETETHAfricaEastern AfricaSub-Saharan AfricaF/?')1FEritreaSovereign countryEritreaERIEritreaEritreaV+@i7. Least developed region5. Low incomeERERIAfricaEastern AfricaSub-Saharan Africa!E/?')1EEthiopiaSovereign countryEthiopiaETHEthiopiaEthiopiaZ 7. Least developed region5. Low incomeETETHAfricaEastern AfricaSub-Saharan Africa Cn'  k re%rDSC0D/?9)ADDjiboutiSovereign countryDjiboutiDJIDjiboutiDjibouti]7. Least developed region4. Lower middle incomeDJDJIAfricaEastern AfricaMiddle East & North Africa>C!/!!!;9'+?CCosta RicaSovereign countryCosta RicaCRICosta RicaCosta Rica@5. Emerging region: G203. Upper middle incomeCRCRINorth AmericaCentral AmericaLatin America & CaribbeanB/?')1BUgandaSovereign countryUgandaUGAUgandaUganda7. Least developed region5. Low incomeUGUGAAfricaEastern AfricaSub-Saharan AfricanAM/M+M?''1ADemocratic Republic of the CongoSovereign countryDemocratic Republic of the CongoCODDem. Rep. CongoDemocratic Republic of the Congo*>P7. Least developed region5. Low incomeCDCODAfricaMiddle AfricaSub-Saharan Africa@/?')1@UgandaSovereign countryUgandaUGAUgandaUganda7. Least developed region5. Low incomeUGUGAAfricaEastern AfricaSub-Saharan Africa?/?''1?ChadSovereign countryChadTCDChadChadx=7. Least developed region5. Low incomeTDTCDAfricaMiddle AfricaSub-Saharan Africa0>/1=9)7>RussiaSovereign countryRussiaRUSRussiaRussian FederationX"3. Emerging region: BRIC3. Upper middle incomeRURUSEuropeEastern EuropeEurope & Central Asia==9%3=ChinaCountryChinaCHNChinaChinaOɜy3. Emerging region: BRIC3. Upper middle incomeCNCHNAsiaEastern AsiaEast Asia & Pacific*<//5;13?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~uy    Z^       $  #  #'+?C?CSXSX!%jnjn   !! "" ## $$%%&#&''T'Y(T(Y)S)W*U*Z+U+Z,$,(-V-[.V.[/%/)0(0,1&1*2&2*3)3-4*4.5,506+6/7+7/8-819-91:.:2;.;2</<3=0=4>0>4?C?G@2@6A1A5B1B5CC D3D7E4E8F5F9G5G9HXH]IXI]JJ"KK"L6L:M7M;N8N<O9O=P:P>Q:Q>R;R?S;S?T<T@U UV VW WX=XAY>YBZ>ZB[ [\@\D]A]E^^__`B`FaBaFbDbHcEcIdEdIeFeJfFfJgGgKhGhKiHiLjIjMkJkNl lmKmOnKnOoLoPpLpPqMqQrOrSsPsTtPtTuQuUvQvUwRwVxRxVyTyXzz{{||}}~U~YVZVZZ_Z_W[W[RWRWX\X\NRY][_]a_cgk`dbfaecgdheifjhl\akolpmqimosnrptqu\`rvswtx vzw{x|y}z~{||}}~~W\^b"&"&          _d_d`e`e  !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKK LLMM NN OO P PQ QRRS ST TU UV VW WXXYYZZ[[\\]]^^__``aabbccddeeffgg hh!ii"jj#kk$l l%m!m&n"n'o#o(p$p)q%q*r&r+s's,t(t-u)u.v*v/w+w0x,x1y-y2z.z3{/{4|0|5}1}6~2~738495:6;7<8=9>:?;@<A=B>C?D@EAFBGCHDIEJFKGLGL^c^cafafY^Y^!HMINJOKPLQMRNSOTPU[`bgQV]bch  "&+-.015:;>?BEFGKLPQRVWXj|}~  GRSTUVXYZ^_`a  "#&*/12459>?BCFIJKOPTUVZ[\nLWXYZ[]^_cdefgdal-grass-2.0.0/autotest/ogr/data/PERMANENT/vector/country_boundaries/coor000066400000000000000000004533111510331751700265550ustar00rootroot00000000000000VV 2[<@:cE_AcZӼcҤtc2}ƒcxz,Cc1|DLc7ۤ4@ 4@4@m25@=$@4@B4@ r4@C5v4@7ۤ4@ PCcp%;6cJhc0BcPCcbE-5@e685@ht5@ AJ5@bE-5@ {crK!c:pcec]c2w-!cLl> cMJcO`c {cyR5@qm5@HȰ5@uT5A5@T[5@U]O5@9zF5@htC5@yR5@ B&9 c-σcc;ǀc%ǝc PScB&9 cZd5@,N76@#Jv<6@N@a#6@rw6@ 5@Zd5@ : d@uu d@|d@ ]|d@: d@02aw0/P=r(0C 002aw0 ?2$3KhyxK`]oK"KB%+CK<Ͷ@-e @b0 @@J㝪~@ AE|`@;f`@A7r져`@UF@׆qF@avG@ ߉*`@708`@ 6=H@8R{H@ T;i`@EB[``@AE|`@dFFF@{F@UF@ lֵF@bF@:9 A@U!ѕB@ `B5H@*U H@8rH@E G@I]'P<@>iN=@1uӈ=@[rI=@ O+B?ao?`"%!@`0ɑ@ @@::1["<1l5@X4@ @GA@p= #A@:^m@@@ 'RQ$7)c# Ywi^!}8@8|Oe;@ 2[<@?K 6>@^ĕPs@N@AN@ l30@@x]@r0m@j@0~5I@p׮ͼI@`(g2ƄI@JTfI@ =?@Hf=@P<@DcQ@/ JQ@ON%DQ@ 2[<@R<@?K 6>@^ĕPs@N@pWs@N@AN@ 4tV_dIڼvdIX^ J@ݮo K@@tK@HRDLK@,3ؐK@ xECmU[acUg\]9UBD+U,ǑU<+ UHTҊTyH-&@A*o&@!N%@u R*&@\~%@$?%@GA$t%@0%@ FD5@P籅s3@8I{3@aK@#WnK@T6K@ T;i`@+Hd`@4HT`@dFFF@1O wE@sE@  !<@xfk;@nJ@+J@ ,7?@hlj>?@ J@^J@  p\@&xie\@&s\@py@F̏@ob~@ 4HT`@ TzT`@sE@ '2E@ {T`@4HT`@t2E@sE@  |=@U=@ 3=@ $fGʿWc? R#TʚT'*TҊTnj!#@ .%h#@(v$@0%@ wocE@8YE@ӏ%@w&@ {GE@xGE@aۢvE@f2]"@Et#@ <; $@ aۢvE@(rGE@wocE@ <; $@@4)%%@ӏ%@ rD@\FcݓD@X4D@a<,@ V+@)Wx*@ X4D@h|?E@J,E@)Wx*@rPL)@@ҩ+)@ J,E@N5RcE@P4MeE@@ҩ+)@A,(@@ 7f)@ rD@k+ݓD@X4D@a<,@+@)Wx*@ a\005c(n0j0+@?큃M*@ fVH,!V5&V|n(;Vu t/@H/@xHyi/@ H/@ 1UU yRU8DU*@p~V*@()@ @[)3@nDp2@ {F@kVF@ ?$Gpg3@g3@BT)nF@ #nF@ g3@@007c3@ #nF@ڿ$FF@ jkeC@CC@x@@@@ kHG@X5RG@E G@TuY=@t5>@[rI=@ E G@GHH@[rI=@&WA=@ PbF@lֵF@ǎЕB@:9 A@ SvA@lm_zA@LV=@*G[=@ :3P4@e4@U+~D@Ӄ ~D@ e4@vq 4@Ӄ ~D@c*E@ vq 4@:3P4@c*E@U+~D@ XA@TGcA@xA@Д~"A@)&RA@ROcNA@,K@@@@ #H5@FD5@%jOL@aK@ xk.@@0.@B1.@P ur4@g 5@0LN5@ >C@4xh C@G@ДAsG@ 4xh C@PyhbC@LqC@ДAsG@vG@H^G@ 6A@T_A@7ւA@uII@%T`cI@HﺑI@ 7ւA@jjZ_A@6A@HﺑI@{FwcI@uII@ x]>x>@H >@NOIL ԇ-I@hޱI@‚>8@ <]8@ 05c(n0̉}m0?큃M*@|D (@ B1.@k+ݳ-@0LN5@pJ6@ _t\,@@Sm,@k0,@(@4c)@Q)@ xk.@S..@B1.@P ur4@}8g 5@0LN5@ 5ad;OaJ1amQ@!rhmQ@ d;Oa je7a!rhmQ@J1amQ@ 9 W=@ xF=@HPYa=@aѦ Y !̶ DW=@ |=@^ v 0B[](@w(@(Эg~f @@0&@0v'@0m^ & ؃m3G@jaF@pQ2F@b*D@4 E@N4[@E@ eo2@nDp2@qUF@kVF@ nDp2@%`3@kVF@evY>G@ @wXH@[5RKH@ԕH@xO.G@J`GG@'>3G@ ԕH@$-KH@@wXH@'>3G@ߡ(GG@xO.G@ &6?@uT5?@hlj>?@ZF J@"T J@^J@ hlj>?@0!N?@^J@,gxJ@ 0!N?@s.Ue?@ʔ~&@@,gxJ@X/XnJ@+J@ :7+@Sn+@iF@R7 F@ PbF@bF@ǎЕB@U!ѕB@ bF@F@U!ѕB@{\B@ ,7?@&6?@ J@ZF J@ &6?@l֨h@@485R4@@ZF J@p= J@H$J@ 485R4@@DMg@@,7?@H$J@͘J@ J@ Adb(?@ 9ԧq>@?%l0! s _'T! 2T!wi^! ;@;@8|Oe;@ 9 W=@P\0=@aѦh+? Hv]@@ A@yA(A@0^YӔA@\yAsA@KJ A@]YA@B'TIA@A@@ #}A@ Hν$ @)] @֌-@P|hBI@6یBI@pi@I@  t#@Kh"@m !@\[}K@`YjK@`.;{K@ j@l,@JTfI@(3cI@ /jE@p}.WE@pME@ +{33F@|uWteF@YˋD@-O^D@ږt D@٣D@oCC@  u=bG@؃m@G@4%^C@` g2bC@ ؃m3G@N@aF@pQ2F@b*D@k E@N4[@E@ pQ2F@:DF@N4[@E@ Į"[E@  D@=C@6E@E@ /jE@@P:z|F@YˋD@ZHŸD@ 30@Vcj^0@4AH0@mG@U7kG@!z@G@ eo2@ao2@%`3@qUF@ .VF@evY>G@ %`3@^84@evY>G@8OPG@ яc6@hg 6@h?m6@G@/ ;H@K 6H@ n[PE_@Z 5{D_@=_@^M"x."7! 'A@* A@rhA@Д~"A@е@@:*ӡ@@Zxީe@@@@ Q= `iA@SvA@| U0=@LV=@ SvA@A@DA@LV=@T?@E 4}?@ r\A@G&A@|!Tn2@@QZ@@ 8sN4s2@R~R2@})3@;E2,_3@bp=E@ $E@Qi>D@X SD@ 03@ 63@+e4@~}XE@xD@E@S:XKE@ +e4@LA4@S:XKE@&1hE@ gI@(cI@hޱI@68@#Ok8@ <]8@ 6q0@JF0@06q0@iLH@Ue)@?%l0!mA '9@H+KC9@Ͼl1"wߍ1 B R@M\R@")(B@ {<µB@ fé$@ i#@@+"@=D#@ig>0#@:_rG@ juG@{(G@> g2~G@"o5G@ l,@d 2!@@l:@(3cI@ݏ I@CH@ Dʾ@@l,@p]H@V!|H@(3cI@ )\!5@0U4@–̬4@=p4@07qg&4@p&zkD@X=JD@(7D@pD@mC@ /l)6@hW{6@\"ja6@v0bCD@@`D@A&xC(E@ \"ja6@?5@-j5@A&xC(E@&E@VcE@ e4@@٠t4@;jv4@`e34@)\!5@Ӄ ~D@0w0}D@8oD@Vv D@p&zkD@ xi!L@D.2L@*>L@yV.L@| L@,"ur9@0V_9@XsZ"O:@@ *[e:@ʂ2:@ | L@xi!L@ʂ2:@,"ur9@ @ K@H >@Z8OV`OIL tJk>@@ O=@DW=@,P'>[E^ v 5a je7aJ1amQ@J1amQ@ Q= `iA@!!A@| U0=@@'8?@  4!+;@s)*;@ !<@(ټM@ؼM@^oM@  !<@ 4!+;@^oM@(ټM@ Hν$ @)] @l30@P|hBI@ AI@0~5I@ l30@Hν$ @0~5I@P|hBI@ (U@WL>@8 ,?@Ր @?@@ J0Zw>@ŏ1w>@ @Ր @ (\2@33333s2@8sN4s2@33333SE@= ףp=E@bp=E@ 8sN4s2@(\2@bp=E@33333SE@ g67^3@6$#g3@?$Gpg3@X9vnF@BnF@BT)nF@ h(D@#v(D@2(D@@cZ$iZ$Z$ S,6C@ C@h(D@ &z3%@cZ$ h(D@2(D@@cZ$Z$ -Gq>@ 9ԧq>@mA s ZrX`@)\X`@(Ux>@iV4@NOZ8O ZrX`@GzT`@ TzT`@SF)E@(\2E@ '2E@ FzVTQ+;QF$QS|QBQvTQ)3@@Fz+3@ L2@|*2@d>Q2@ HV 2@ Dʾ@(2N@@l:@p]H@vQH@CH@ ³&olLf&` {K&hD%d%P{u$Plc$@=bl@@M 7:@a@, @ D&4J1vC=K?2$3K V̏@p(G@<Ͷ@ :3V7>;,^VDK6V.AV+IV:(IVq2@JV|2@lF1@ܙ c2@T1@(Xg1@ DA@VA@r\A@E 4}?@t9;R?@|!Tn2@@ c);@ z;@t" <@[mގ<@nV!E@{`IE@tE@fE@ X`@i]`@`@k)`@ .`@/ `@H}`@ a@UDGa@N(DQa@Ta@j&ka@E?a@0"a@k a@+|a@-iYa@48EEa@#0&a@֨hta@b `@?ޫ^a@ϛTa@eN/b@aob@q&b@b@'r3܀b@>٬c@fffffac@'XFc@7܀c@c@c*d@öE Ud@CDhd@`TR'd@nud@n;d@ / d@{^cc@ cc@Bc@YB}c@mc@Kc@C ףpc@Ec@oec@suc@d@7 d@ssczCd@x=\r6d@%Dd@1ad@иp $fd@i5$Ad@@d@Wfd@!Aqd@ c Ad@Gd@7ypd@fk}e@AH@dE@|~!DF@4rhE@ԶE E@ʄ_ E@uC@7D@j+C@dzB@EB@ā,B@"B@($B@/ xA@\ upA@#bJhA@ ܺ@@$AJ@@io1C@%D@Lw-!D@ѩD@ek%D@pAB@ؙB@@\ @@G`?@=?@ЬL)E@t//FE@JvlE@|4fE@׻?gE@WxE@;OE@}?5^F@fj'G@$G@f-9H@d~RI@ꭁI@ J@ZwJ@`Z*oGK@@* K@*J@^J@,[ AMK@4c]K@48EL@>?M@,M@Ll> M@܀M@>M@4cM@H.!qM@ǺM@= M@:]N@4 N@D!TEN@ ףp=N@@;O@lsczFO@ N@Cl+N@#bJM@B`"M@_{fIL@lV}L@3KbL@eNŰK@=yXJ@I@vOjI@"I@HzJ@KRJ@d,K@/>:umK@)K@L@aL@ML@HhL@#M@,`M@ڧ1M@vM@GzN@4M@vOjIN@LM@<+N@yN@swN@ׁsN@&䃞BO@l[&O@pHO@2}}O@pɎ@O@B P@iƢ&P@ K<"P@`7l[>P@$ >P@7=Q@YQ@/$xQ@<1PtQ@.Q5Q@iQ@]P@Q@}8g,Q@3mJeQ@5^Q@0 GjQ@S.iQ@@e[Q@n4nQ@LOXQ@)WxQ@Q@`Q@ TƿQ@ R@X»\6R@aۢR@R8Q@ 1Q@F?Q@-Q@XQ@LioQ@N ^Q@xqZQ@QQ@䠄R@9zBR@ 0dR@p= cR@D oR@'Nw(>R@HzGR@!X4eR@2ı.pR@BfR@!uqUR@h~R@pUjrR@(\R@QR@jR@< R@6}R@׻?R@<.9S@= ףp-S@c]K.S@QS@ƅ!Y>S@`TR'HS@KRWS@[ΥlS@,gRS@4)7S@ S@4ؙS@ m9R@0\S@&SS@=~R@)\R@s,&6R@}ЖsR@<;kR@{R@JasR@nffffvR@Q|iR@XjM%R@Q@G|R@:3PR@ Q@@Q@!vyQ@ `<R@CԷ6R@HcC5R@R@8pnQ@OnQ@C+hQ@8̒DQ@vN?Q@,Q@\P@%1P@ `C@L#J{C@LC@Tt3G@ Hv]@@ *w @@S>@@vj}@@p8A@-w@@*^A@6A@t>YA@DR3IA@njO2}A@@ #}A@ -}4C@&pnB@(rKB@mV}B@+5{|B@ 'B@4hnB@0:1@,^2@̒52@ypw3@\wb4@Z5@6@ ^yH@D^yH@{0H@H@@`oHG@xRG@ =IF@> NF@/0F@bFcUE@>E@8YE@^!m<&@@H8=&@@jZ"?&@|Db&@u $A&@@%@de%@h$@;$@4ct%@w/&@w&@ ̉}m0Y"$0+O0,NKݕP00`RRvT/'B.|D (@⪁W(@d'@3'@h '@aʆ&@hBװ&@ ;E2,_3@>,_3@:?3@` Og3@i׭Q3@\)g3@=3@0O3@07qg&4@X SD@(zSD@(D@xtlD@{]D@W D@XC@5&C@mC@ 2(D@ $(~C@[ C@d*ߗC@1߄BC@mC@p(C@QC@5)^fC@^C@nLOXC@Z$Q|a2$ 9" GĔ ) ^)ffffff(\@jâۧ1 Ld@݀ Cd@5[ 4[=d@ T;*d@sf6d@Ld@8˜%x7!&$f$ؑ/h$@~أ%8˜% Qܪc@::c@w4zc@}b^c@;X 4c@!oݜc@Qܪc@T{*dpOX(P&e4pS>T{ D&4JfAK ZpKSCK(E2ߒL V̏@1p@@0Nm@pg@j@ 5&f'6[@zqiu![@[Sw[@j.%+h[@z^|x[@9#k[@@6E[@vgi[@mO\@~GGs\@SÉ\@Ό\@6\@Bt/R]@:9. ]@Mx]@2)oO^@bz H^@y/k^@BW 5|^@61^@9`^@ P^@2W 5y^@dz^@*N^@t '^@V~iď^@.d]@V9.]@(^@ݞF^@ G^@}r^@ڗBm^@*4^@<¶]@J?X]@Ns3]@NѾ҃]@=b]@d]@'o]@Bݕ=]@>611^@]Gh^@ZBʊ^@7X^@e^@}C^@h^@^iF^@jVp_@`h5@+]'5@yx"e5@*5@ OH4@MW4@;n8e5@8+]'5@qk& 6@mg_6@0&WH96@6@@daj6@ ǼSߟ7@!8@G4ˣ9@x ;@@Hy"<@@19<@h=@ x =@o$>@nE>@@O>@к&W2?@::@@uAs8@@8\[.A@xCvtA@(% A@ЮtCB@I^SB@^wB@Gq,B@~AsB@SH-jB@d/&B@xCgB@4B@"nC@j^C@t%C@xAsLC@3C@п$KD@ܓM#yD@lw͘6D@lDC@ĂAs0C@/TrC@]ѕC@|C@C@ ߉*`@Pp`@_`@A7r져`@ 6=H@/L G@m2G@avG@ 뎃W@?+V@f@h3V@¶U@,}fI@jx*I@!:H@c H@ .¶U@PV@[Ь6V@q V@!V@+:}V@fwV@ϪW@Z/W^W@l@hW@6DأW@"HW@%\X@ \X@e'X@R"6Y@bPuY@V^Y@96m!Z@u=Z@E FZ@:ZZ@*'O[@_[@z3¶L[@N[@K 5[@6K[@ҼJ?[@}r\@$]\@,v\@ \@w̍-]@vf'Z]@2]@ s]@}u]@2u]@Nn]@Z R]@l]@z'\@*N\@ne|F ]@Qyq+]@c H@"g2LH@lH@Pم7G@h-ŮqG@)F@X) F@ jϺF@8-E|F@5-F@$"F@еE@4\E@4_E@)CE@x;&TE@,\[AE@'D@lDD@wD@t1E@`jO=E@ͅ7}BE@coE@ #E@4E@S] F@As:F@,F@i}F@xjωgF@:F@ Zq F@<읰1G@XU7VG@wYgG@tXG@K&G@ީG@(H@TvNG@F&3G@0bMG@{;G@`WfG@࠵M/mG@{G@o(F@{+FF@`|F@rYAF@NF@H Z$F@hF@hQE@XRjeE@PD8E@8\HyE@CCD@ 9T@R T@_o7T@pS@ĦΜ|T@*b _T@j T@>ໃJU@b@hnU@+*qU@"ZRU@ U@,E@ߖuE@eE@g2quF@\\[F@茤<F@xdv>G@hqQG@;OG@`q V:H@cKFH@ }H@ U@49.U@*bU@SeGU@_U@0T@3G"T@d|T@doa$T@sFT@>X3BsS@zb!S@9S@ }H@@:zH@H@I@'I@qI@%ڈI@Peq gI@(ƝI@`)(nI@hóJ@< K@>K@ R>[R@$`R@dQyXR@hHiDQ@@`Q@`'\DQ@xqb Q@@|J@HK@pY>60K@}K@SԹK@{OK@5|K@ xr\5DK@HM*J@c҈!bI@ YH@1IH@P_UG@xN4`G@sG@~;G@`b[I@xI@oI@tMI@,H@3:I@ ͘H@8vH@cs2H@ %!@F @Ыf}!@@4jo!@`- "@`##@3u^L#@f@h(@0f(h@8;@LMO @ U@٥[E@ | L@u 1K@IXK@Hu,K@~J@ CIJ@`68@ (96@4O6@`96@9d6@I6@֐5@ 6@ΥK6@.6@"6@KԲM6@+e6@ ϽK7@`&9 7@+p7@H&@ n&@k['@8b->(@_)J)@R%-)@E 2)@K*@ꭁ+@VHI/,@[wT,@y-@\wT\/@b->8/@@O3@ xECmUR:E|Uzg422jU-**[rUq V sU|SJjUή-UU^GUSS:U-8q>U-UEs)UƕjUy;TM3}T&)TʈTq lTCT0ՈTo  UU> !U`tL>UL'KU 7j\UykUa UPUpPs8U?͹U`kU>wUPMgUKU VfV #R-@1Ƴ.@Z'.@@HyK/@<͙/@?/@z/@ s6}/@/@ E/@dFb0@r/@;ϐ/@`o/@@/@.4/@/@G/@Ԟf`/@u t/@ lֵF@ah G@ͬmG@F@!VF@ G@60֪G@D1G@{WtG@oH@L_H@GHH@:9 A@`3A@ OA@_A@j@@-`x6@@ȖH <@@@Í?@&W>@1>@,5s>@&WA=@ GHH@,AM}xH@`P:H@s㙸I@Af-mI@ YI@g҈=J@,J@$ [K@n҈K@\Nf>L@) B|L@ l"ٲL@%LCM@6+{M@[yN@&WA=@q,Q>@(qW=@@k%>@dk<@q՝;@ ;@`7h:@d {:@ Tn8:@<Ԟ$;@` R:@Nn8j9@!ur&9@8Qa9@&W9@ J@)+{J@!J@DkI@l,I@9V䌙H@,NqH@H)(vB@VU!{B@XjYB@{`oB@B@B@(C@ L&q=@pU`>@^>@Q?@¿?@4\Wt7@@3H@@|3P@@HIFc":@@PM@@w?)ȩ@@(@@aKA@'*B@vCB@0I GA@K,A@Ԉ.{A@hҔ~nA@HBaB@TC@o:F@yR&G@ȋJG@x<@h}Z<@P3<@q7<@\c`;@L7h:@U9@dÍ[9@y09@Y8@0.I8@$8@XǼS>7@@RPd6@eHEܣ5@{FJ5@({V4@7pش,4@pL|3@P)2@B1@]y1@Tgg1@;N0@X7hY0@ 몚K@lހK@~J@M I@ԇ-I@U6@#)76@K7@!ߣ8@‚>8@  v0艸}a0}g/1B:1hH 1a\0 #"0@B8>/@R-@Uu-@ Ft=,@j0+@ )~.@`ljә.@"d_/@``Yv/@xk.@Ф0@z 1@ 3@cg:c4@P ur4@ ao?uS8?a?>?Fd8?`0ɑ@<T@љA"@S"@ ?:#@Y$@Xl1 $@|D%@ 4H~:@ x:@H9@pt{N9@B`s8@X;{7@rlһK@rΝfK@)-EilK@As&$K@P< J@:J@ 7@hmL8@h &`8@'DS9@tV:@˧=t;@Xt{=<@13q<@8t*<@(CA=@ߋ)H(>@h )>@>@X=qs>@&6?@ I@DOgI@I@({`I@oI@͘I@+>I@XKTI@KI@ɪM"I@jDI@ }I@MеQI@4>kJ@ZF J@ FVAsV ynPVj4r5V iV` U1UUx,~x+@`d +@@n*@P%*@^rmS*@xF'KL*@*@ @h@ Uӵ@0>R%!@KY4'@u'3@X{O3@[#ϐ5@TBx7@ @ @1ʳ@oPR@?sYv @!>G@VC@(K @x?PC?`lPoVL%[^$yB@^qB@$p'B@)B@S[B@^nB@3VdB@ׅMB@>7&B@A@ ~)A@xA@ wi^!,_!H5W'h8'!)Za<*6?)tp^0["<18|Oe;@,ʂ9@K59@@7h_7@`H7@n6@$S5@TU5@X4@ ["<1dY"L0e G0`0A0Aw%00BE0K0 v0X4@`_4@<9 o4@7h3@#]3@ur2@6*1@\10@ #"0@ v04_0Pm:+Q?/[E.S)'-y2,D* L) lW( #"0@Lt0@ߌ^0@ E#X0@'0@M0@  0@Ft}.@;-@ -w@@<ߥM@@6IA@ȱg@@d6y@@ [Wtf@@Hv]@@@ #}A@\"tA@A@XCqůA@3A@ݟA@*^A@ `DG=,@̀K+@q )@6'@@\%@ au%@U#@ t#@xPTJ@h!W K@`~5 @B>@7f@%gܞ@`.;{K@PU&2K@}K@J@)(J@`'-J@x}J@ =C@kWt#)D@_pD@{N D@Tߔ~D@P:D@E@ ŮwE@DE@41OgRE@еAD@rv0bD@ n[PE_@sXA{_@"_@lg_@ QyC_@tzB_@z՜|_@`hE_@=_@^M"leF6"s%V!LGCm r< f P; PSP!7! =_@w^@bKZ3^@up^@z\k^@ns_@n[PE_@7!pi~"#bz$ȋvGP$2G$^M" 8I{3@hN=2@–2@@Dq1@3 ]0@Q-@`DG=,@T6K@'8K@27_WK@V!lK@_.AK@T&}K@xPTJ@ `DG=,@`,@d'&,@?7 ,@,ʻ^-@@ɿ6-@:.@xPTJ@`ۮğJ@ɝ}J@OJ@]e J@cRbI@N3I@ hޱI@r\I@8fI@CƪI@I@Rw8_I@gI@ <]8@0.679@ 7#9@A?U:@5:@y<]'{9@68@ JFcAj@@T>u@@$ɆT@@ 4|/3Ҽ 3,a2\GI2F681Kp1ĄS0„S0i.\!.b-}Jig,}T%BG)B!'<%@cZ$ @GA@Z=#A@@GA@A҈tA@xOFcLA@V(@@x@@ jkeC@bɀD@B%D@k"D@t$D@D/f,E@x@@~5A@&NHlA@ -B@V'음MB@BNvlB@ 0Zی:@`2:@|3)9@=.59@0U8@ 5&f8@)7@,i$7@яc6@K@H@f,>EH@lZG@z #G@yY>gG@4+G@(G@3=OT H@G@ %fPX0@@\1@Y}1@ݱ!1@X\82@w?2@:AH1@țmA@2@?~>2@H(`2@_cjl2@D2@vz13@@̝3@H)>4@"e4@H5@P6@6@:7@@ɇ8@=@,9@mA9@"9@G:ak:@8yv;@GrA8<@&<@@cjC>@?p>@~>@WS?@RY`?@ȼ @@u?)';@@LFJ@@JFcAj@@b=j㜞= ȹ>ѭ`?6@/>N@Q@@ {@EyAK@8Apq6;ApqhA@8 eAf5Av:!!A9A@H_fM@"V@5[@ :@M)@Na@K@X& @Yb@pq@>#?|l>h=wf=A=>3<M< ?\x;E:  Q3@_)l*4@(60744@.ɝ4@icj4@x65@e 6@\0\6@7q6@(7YO7@x:˻7@ ;Y68@>Yq9@AY'9@q=9@@9@~RZ|:@h Q:@;@KYi<@1Dn=@jJ88b1>90,a:z:^d:p:lw ܾG:y92@9r D9,c9$sB9LL<9|9D,9 E8[_ǝ8J=897 5ڨ6E`6 5F>@t{->@Tw?)e@@ /4SJN-L+ -Gq>@hxX>@d۾=@?Y07 <@฿*s<@fv<@[m~<@гK_<@ xu<@`ӧoW=@D=@`D=@wL\-<@w"<@3'<@~Rc;@cj*;@>&:@x>\9@C k9@y}8@d"P8@1A8@/7@mA Wy ?wf $ʑ/ !',pT"p=;6#Lk%aX',sq'm(X[(,e6*`ةzw*өze)H(ΩzC(Y07'(,P'B!'Vlpp& 6z&4/&Ek%%  t#@qL#@:o$@sgU%@&q %@:o$@ &$@>Y4%@ ڂ(%@`#@&T"@q=!@PЫ^ @0q. @"d= @m !@\[}K@ (K@ )(RL@,-j L@̂:L@{NL@B qL@$V!L@f.qL@H=VL@FxCL@L@gL@EL@ܢDK@`.;{K@ 07qg&4@74@ȏc4@p]5@8ЅK5@Dۆ5@t{r}6@PO{'7@dd6@8]h7@3p7@)> 8@fh8@81&7@0 6@ԏcY7@M6@KU6@S~a6@ `W7@P%Kd7@rh8@q7@f8@Ur9@h"Ó:@mC@txjόC@@{bC@ TH'C@觝B@@R(lB@8k0z4B@g6B@, B@tzB@T"B@RB@(C@,ŅGAC@F|C@ܿKJRC@LdC@|3 D@ā_m0@Y0@ܽҾ0@"-@0UG+@kG>*@W(@d&@9wY%@Y#@ *(k9ؗQOڅQօQ|ڙQ@geQdQmZQnQQQQ듞&Qvf#R|&~JR ʢeRVd [`R>> MRUkR*5&RP]>RʮlRqXR-R&aORhgR4(?LR=RvRΜRʈ-RЁJRg%R*DRŗRSlRSlRNc V#RƀQ*$gQ^g%QV|Qrʮ]Q3$QhY2dJ34}d5|7<>=r9l;ys]>N5@X&]@sw!A$m؏B"B(5$C촵CnCnDn˜E6\ѮEhK E1E8X&<:F40 FF?F:ARGt9xGv,- GI0>VH1{n0I cI7KIL!J5AjJ HjJc/>J1@OJ>JJ.HsJDJQ^%J-T&J |']Tq`FTLZTeDI;TzOTLPS0Sxp]SX0S *FSԫiSSɮЖSd-x9R-;,s\R|Q92uQ(k9ؗQ~b%= hgED%OqzrPQN!`)|Q*kN8*e,99 Pc$Q<r(m +Wx+H DoL-k.Vq4[0 ]1ڨ1hY2 4!+;@87K*;@v:@a~;9@@@ɭ8@Y3V7@ 3m7@`8@QF07m8@"P8@(ټM@RټM@cM@l 58M@JM@}M@!nNM@{t M@fP1M@ROL@ Sn+@x[+@ _P+@H+@@Uz,@ Ԟ-@`^2-@.@RY.@>0@/0@0[cj1@mD4s2@8sN4s2@R7 F@͘F@F@8fF@NeF@ WF@ٿ^F@As(F@xd F@oE@:E@8lE@ \[p=E@bp=E@ ,\@t^/g@0~Sl @@~#!@g#@k0jf$@%@jAb&@ 6(@Ы)@КBA+@`,@\h-@0:-@.@`Sp/@Q30@ñ/@C<`/@0C@^/@HG0@&0@رU 1@+1@3r0@_ם0@ mt{1@x1@0p0@пSX/@%`~+0@PV5/@@mH.@puD,@) +@`e|-)@,;݅(@B(@;(@,H*@:7+@E@3O1E@`|0F@4h.F@XȤF@E@):wE@Tî~-E@g2.D@|dD@4ﺺD@nV!E@  je7a0)ŸaҺaiezja.9}?aa1%JaJ1amQ@wP@ٝ8'N@Tnlo#N@<N@`q8M@ c'Q`z]zP`Bxq)`"[='$`ȓk_1=a__K8g_E_A:_[tY^(\µ^HfK@Ƨ$K@]].J@J`J@70*J@rCQI@ݰmQjI@/iQ5I@yH@Gl(SH@H@ - PSP䃞ͪPh:;P2YPPrPLWP PjWVONBxqM(~k9N6LjjZ_4L^xKƧKՕ<LwL{/LL}kLNM;NO}:NI&²NhUM@O^I O#EdX%PvݰOP)?PP-Q-!lRQ|gQpeQ5vQ1ZGUQ[[%:R $ uRêR{IcRjHZSDֆS=>tqSvyUS7ُSS:XSSnLOX'SFB[Υ"S_vOFS_SZSSHS@j'S-lSSYT$STgT_vTN@T~@U{IcUPLۿU;iVM֨BVsVWw0W>MWݵ|ЫWBsFW/>:uW JW[='V('UHVpB!V߉Y/zVQV0U@UjMSUSW>qU4 /UbE T;SXTq $ PTd}TNTNQT2YاTGɫsUgaU0dudUZUn/iUQVBBVoMVZKV鷯V/ Wal!AV48WDxW٬\W%uXk)X vWvۅWp= ׫W4RWjXXTjX6Xec]X$(~X"]Y`TR'YrCQZLUZZ̼Zd [g~54[[닄Z|a2U8[Xs`|[Pn[ ҌE_\(XQ\LqUwy\yX5\T5A}]}"Of]\[%X]`9^^9v^$^_+j0_Sgp_X4_:}k`VF `Y/r#`yCn9`|m`~j`D`9`nSaz{1arPLca5aBF@L1F@ڏaF@[Z ;F@0E@'fE@ !F@%UF@ˡEF@ͪF@NE*-$G@"TG@̗`F@5F@&6׆G@^G@u H@ _H@ٔ+˝H@~k H@fffff&H@g67~G@e-iG@S:XG@b->H@NzH@NI@gj+&I@E )?%I@nQfL I@9]I@~:I@bI@{/LI@$zJ@(J@W>J@5J@x&1PK@XyK@ <K@YBK@HPs+L@Af{L@hbM@ԚM@I*N@l&lsM@g67bM@e6$M@S"fM@?ŊzM@QN@jH܇N@[ɎN@z6>N@6 r O@:p8O@A}˜.O@kw#O@û\wFO@)?(O@p%;6aN@ S"M@9zfM@m9M@ L@cE aDL@9@0K@ŏ1wK@LqUwUK@uK@+:uP@x]'Q@FPQ@UtQ@jeQ@{/LQ@)?Q@&NwQ@KQ@"2kQ@XjDQ@@#S>@n=@6!<@CRs<@&u;@If~:@ 1*9@@'8?@+0du>@Жs)?@4iB?@h>@\Cm?@'IL?@) 0y?@9/?@ǘ>@7ܘ?@Qi>R?@?@|г?@ 9@Q=@33333s@@4hnB@6@6@6@6@ 4hnB@M֨hXB@, PSA@ɰ72A@1A@eA@Gŧ@ xK?@8Oa*<@DY^:@D8@X7@гD`5@st{/3@o0@@A鴅-@@,;y(@/%@PEX!@t^Ϧ@jP@Xҡ;@`MqЩ@01@Q @s)$@B&@DcQ@NDQ@ Q@Q@ GjQ@ ӏQ@Q@TQ@XNQtQ@6 $Q@MAP@ƅmMxP@j}t@P@όO@tO NO@طީIN@M@HHKM@{ M@B>(M@ "M@HMmM@ ؃m3G@LG@1vQG@|5@G@~6F@< ԛF@@P:z|F@b*D@dKʄD@T AD@ز:?D@TۏD@XotD@ZHŸD@ ,NqH@ڢmH@coH@ ڗH@BMH@i G2I@$ȱ I@tr8H@)H@ΒJH@(C@\ahC@LC@Dž7"C@$zD@p3 D@/YCD@cUID@H?`!D@b7D@ P:D@ /D@$t.C@G,C@|YtB@<>5RwA@)$A@@L|y,@@([%?@(p==@:<@cG;@Ö+:@3:@(׸vQ:@P:| ;@`$;@ <@,1=@H d>@*`">@ >?@d.,A@@LaKqA@*{v[A@4A@͕B@,r\A@ ƒm+B@rv0bD@D@4T'D@T -EkyD@DD@&E@ݖHmE@8/O=D@HB.7D@$)(D@Xt:D@5D@ οWC@Xc-~C@B7C@-TB@ ҖHTTB@, iVB@PMzB@PY!B@FVB@T?7wRB@Ȯt B@t'B@3eB@D"n`HB@H.& GSB@ 3#B@ (A@ ?$Gpg3@07q2@cם1@,j1@0@ rQ0@02/@@pj/@%`Z=0@6qt0@:0@0= L1@ӧǬ1@(\2@BT)nF@pqF@ӅF@NeF@wF@:9F@NeF@8@&hF@*xC,F@H WGF@"wE@/!E@遼E@33333SE@ TzT`@u ?`@d3`@I-8`@+]{`@b_@Vj_@z_@iE_@jVp_@ '2E@)(!~E@$e6E@̅DD@r+D@FgD@hWD@h*0D@ HD@C@ jVp_@@2/_@;)T_@!X_@H_@@h4N_@?_@b -_@_@U]O_@Q_@Nad_@plFl_@J?/_@C@̆)C@D-œC@>]C@؋lC@vj:UC@2(Og4FC@NՖH C@OrB@`B@vYB@qDB@hL@RB@<B@ 0 `@>I"_@12_@,"_@#_@d(_@⹍D`@1U `@u &`@d6`@[5`@0o >`@no L`@ZrX`@!]NC@<>փC@8DSC@vDvC@SvC@tȅ@D@+OLD@Q -">D@B0TD@qD@L~D@mY~D@t)(#E@SF)E@ 8tcj 77@Ö6@0ԧI5@,5@p!8@8P7@=t8@xv7@=8@/7@(210 p40) h t)0+ɶ!)1sa(zq'A(y&% /7@>t7@0D\6@6g6@`ԧ'6@Fp56@pV 5@EB5@>5@N5@ xe5@@ƃ4@(C4@Ht4@307 4@7qj3@,*3@hI3@,07v2@LRY\"2@PMy1@0 1@ x50@?컒0@\S0@@ >LN*@p: *@`hx)@w(@%XbX%h &bt%o k+&."#vm# #P^A! o *Prz)n2E`iKb'w`;( lPxWcPLa(!# Pnu.07 `5}9*yѦt{ѦPЭg~f w(@ft(@@Dt)@0x)@> y*@P?)@'`n0)@=*@ b0O*@`ym_+@ۂ:z+@` wD+@ǖ>6*@z)@d| )@pxY(@@yRE?(@``n'@ЫG'@dAw'@Эg~f`Ѧ-}_@b  !Ț^ !(UxU"-#"sE$COv%g}&)9=(i(0YF* l+T%,ݭ-o o/ S0 HM1 KToP̜P`P33333CP̜PPQ&3(Q\rKKKYK9KJ̌JπRtQJ ,7LPP#5P|ӺQ:&>Q3&Qo'QQ;@Q^ iQFjހQdzQǑ>Q&„QK]N%tQ{NtQb؂՘QE#aXQQ.قQ`'u#QM]wQrJQb/bQ(Q6͋ R"N]NQDQxQtQjBQl92Q^nTQ\QR-xR=L1R60|)RښZRX>TR0آr>R^*R@Rg%Q:A*_Q3$Q貋e6V6Ez8b8=;]/:ā:lQ ;:pEn;Eu5Pq]? \ì@`K@n%AMRAGh4CH)vpHޟ¨HI|0I#(^I^AVI秈IxP'JIvUEJ-T&J &3$QZ Q\X(24Qf|HQj£.Q2:PКP PsViP\ V6P:';'PrLP§GPp]-dPYq UPKPR>P˭OOn> kPbq>PGPF7.P}O.wv_OOZN*OlO'+OdNl.~MaLAMLDCj?M]6M-T&JA,J8GIh~]I(8!INOQH YBYHNНHh 9G죤WG&GgUFvFRtcF9\,@F䣤hE5\oE.'HE녗E|=>-EВlELDx;D&fD(YYD|tDL-VDVNODIChNOiC~:НvCc,-0\C&CVKfsBNO4B&A tޤAt;7Ai@ bNNyɑNpg51N,dyiPpݏPҕxvwoPҕxvwoP0P4r=Pr O0PoN`&M}f:MbNNH7TFZ@TJ,-#T&ITHʧ^ʧ^d-rg^`^}ơ]o\X]u]AgM^5t:`RTRb2jR.:jR2׃RR̟wauRy~^R +jR5t:`R cc_u_m+1_._*az_9B`'_ IZ _cc_y~^RH$c)OR㻛]R@]R6ɏoR̟wauR8pwRy~^R UFѾXK`+Y mYCTu*YCTu*YWQsYg#!$2Y"OXxgeXƴ>XtLNRŜzRŜzR<6uRZߜ Rv)R 9DRQp0R\Qg}Q12Qt Qd]QOQf3,Qj}kTQفQgQPQfqH=8QH8EBQX)^`Q>TɓQ?Q8Q¶Q^f RicRڵU RQaNQf3,Q ,r;NM@LHWSLгLtyuLM1ϔM MՄZN[NUOi5-OIgO2|-xL P$H^$P8{WPܢP SP^P4>oP S7PaPYqhPx.uzPvC#Q2P"QoQ*H QkPܼ'P^P` PBkPzJ9P ;Q`QNx6QX5RX8vRbQ]RNE3SS]N:SjBA{S4;,SFFL=TH +Tzp#^T4T^QTjAuMLUU n,U"V&^NVb]έVԅV]#WkW?W`lĊXRlXd#ᛇX~ 1XtYE?fY(|Yw;YE Ys@Yo]Y >I,PYx40YjExYH`P)YiZqYN-xY8Z(gZ1UZF-[U;>[a.[q-$\NdH<\&>S\>o|\~a\ ]̶-8^]@= ɫ])k]D^-VN^YW_&Y_J_:\+Ǿ`h1`P&]`o<:`ξWfܷ`;V``܂M a.20aO 9aq[as.aDpa&!VkaJ N bhȣbpXRyFbH=Ob ȗDbLoQWCbvsbnVbbhTb/rcvH7c~4[@jc/cX}cw.cZckvcs8c~4[@jcŃ},cHsNbTbvbX/Xb$3^Mb%bgbBb$оbe crMcWc̚c.'IicёpPcsch_TLc^R5!cNC-c(2c.bWbubZbLaqnradݢreDseKںe]v=e eetOeţf|$fy7(f|0JafDh(~ffff@f@gHf@e@9+e@uCe@ue@d,e@m(PJe@B8Td@|d@vd@[ 4Od@#4d@=җ+#d@/ 9c@38$ d@G?8d@7㨼d@q?98d@QyQud@s:4d@ύpd@B8Td@tʗd@3dd@fځd@k+X@^AGW@9 5 W@:W@NcW@~L&W@e1V@WV@2sjV@9G5V@:JX3V@J$ZU@R2tU@&U@r%iU@zF+U@rT@BT@e'PT@p^T@Si;T@<T@>uIS@_mjS@F GS@u(FiS@>(S@)R@vϪuR@^#ZwR@2ZUR@zs5ER@JsR@>Q@ Q@ lQ@FvwQ@O@h-Q@BHP@ւsQ@[Pw;Q@^>DQ@BHP@J_P@T)&Q@⊭cQ@kQ@@>@>v&=@hU<@t;@P09@w3c8@7@0fđ6@K5@Cs5@f0`4@>fB3@|U32@1@*/@ז>@.@PʿQx-@JYv*@x(@`['@`"d%@ :$@ 3u #@ڂf @`@ m$@@a|@tS@`,}d@ƚ\.@ /?CA?TCͿg忁eUhPd/]z,u?%`f)6yL 잪9!PC3"0go$.v &pn''PDZF(0t"ຟ*}"N,0w5.jb0Dt9w0h.@x)g/d$?0!1:2?4`955fu6 t7p\y9 ):@4+;<0V=$H@>;.@0o@<&C̩A#^ uAZ.wA썣AH5@p?\=\= 6AA=wֿ<=i>ɷ'@$su1Ar;""BXK/VCNRmDRmGRΈR)R+S\oaRjÙRfVXRW|RaSpS$SM"*:SpKSYSңwW_S%r2DS fmLS2ڠ?SS2.?Sf8SS78S ޽HSBSتDbSRR)S S:RһS_JSZȠTe*TӠBTswUT~D@T}{~LJTNIFTFgZT2\-qT1wTyTNT;Tr~Tp T2Ue"Uj$U{BU*TU=yfUE'RU/0_FU=$WU*)HUV"<4Uj$Uf1UU TmmUU%Y2"UUwU3BZU#U%UU ,.Ug-UVVg-U"V"=U)- Umm:zUUcpצUeTTݪDpT! _T"5NTnZT(lTJ&)QT%YM8ORL9R7;RRRyQ]N{Q+~9Q޽,Q 6eQCKaQف.Q=߭Qzh Q:wQO%QZxQ^cfQ]m^QH8XQ)IQjc){5QwU5#Q-׃?9QK\7Q)w-QwU5#Q/t:Qc)VQ IPJyP"GP*@PVN{P^PP撿OP撿OPGAPdD$TP"MP'ͱPO\|PjgZ&P,tMeP%SPqڠnPe@pP9S3sPgڸPgڸP¹y;P=jCP#PZGP@P@Pw PƧPe|"P@PGAPN#P!`(P RP*@PZGP;ɬPgڸP֓ Pj~xP *P|kP¹y;P;ɬPVN{P"MP"MPf=ջP21PF,b~P:ݽlPw dPi/vP=jCP*@P}PV$P}P}P˪P RPV$P8KdPN#PV$P"GPP\P"MPe|"Pԁ.oPGAPP\P&P8KdPM%P8KdP5mPN{P2P\mQGQոB,Qu~DQ[p]QgQn~+oQZwWqQ%wQR,LQ=߭QT\Q(QyQRRᤱDRK RRBQGAFQrQJ\QQ.׃QrH{QhkQqHNQm` 7B>QsPsPwU5PG̪ AP@PbmPJ\oEPsQ} P a~PbmPRBiPNP뱈P&:PF,b~Pj~xPZtP{t^yP=gP&:P^GPGAPV$P&P5mP6'PJyPJ\oEPFQQ_ 7QBcQ n&Q4q;QjD GQE"bQZwWqQfF_aQfJQ+OQf@Qn}1*Qe* Qjc){5QH8XQ,'jQ{tlpQrH{Q)QiD$Q]N{Q6?QQ(QyQ&Q(Q@QZQ_ RXRR_ -Rva'R.}(RX4RaSS\oS S SKemS T+S%r2DSbIWS iSpxS7&S% =S~Z]SB1SB1SRR)SnXS UzSiUo SZȠT*f%T)Ƭ71T"jm9T),GTswUTZVTe1iT*a>T0vTimT9;׽nT9T6ꆐT=T[G{gTFTJK&T T1wTvӠoTvO]^TR"TTT~D@TH3,T(,T+ccTo'S=٪So'S S78S7&S:t:/SfmzSofmcS_ QSw~FS$-S$-SN(S T+SN(S_ESVzS>R/BKޥRN{RjAKހRyRw&RMpԆR,~lRj̬JR1RO\oxR_ RҭQ=߭Q荘QZxQgQqHNQ>xU~:Q6H8s+QNaQ} P9PP\PVN{P?P¹y;P>K.PzԁvP=K _P3EP馈\$3PwK"P?"Pe+t P*(wPh~ P p= ;Q@Q7Q@Gz.Q@Hz/Q@RQ@p= ףQ@Q@QeQ@p= ;Q@PH ףp=jHףp= H33333H{GHq= ףHQHQxHPH ڨ<,b@sBb@pb@XZaq)b@]-b@3nOAb@39Ub@KE[b@nrb@?}b@hλb@b@)* @b@ivb@]b@oKb@ڨ<,b@f:reDH@ZD|ΔD\RMEr XEP{^EjYE"{:E|<0xE)EY4E 4ET*"VpDugDS~DDf:reD Ta@٨a@-a@=Ta@a@%Sa@1rBVԴa@x|âa@/Va@!85a@a1Hxa@uب%^@pX3^@rC^@u^@ X^@n}rw^@"Pi^@RJ?Y^@[P6^@Έ]@.N(]@v;8]@BWk]@Z ]@bcFB\]@=_J]@s-]@j\@las\@"t\@j"ok\@s\@~#o\@D6u\@^?m\@zb|!`\@Zb|/Y\@TGh\@ )n\@,Ս\@> \@"/{\@>,8\\@Μ(q\@@hU\@·R^\@i \@"R\@gXt\@s\@ |\@*u̍Ͽ\@҉zUL\@Z"\@f'N\@ z\@86\@L\@\@;\@B\@2(]@Z R]@A#]@9]]@eFx]@*!]@L2]@J(#%^@J%S^@vS^@:C^@7^@jB_@&{/_@'E_@_@la_@µ`@?9%1`@oLqj`@7`@'q`@n`@9?`@;`@L`@L`@]>`@I a@a@M 49a@{ ^>+DȜ? {?@zH?s@vΣ9@2Dxv1A_`6A:BAƊ)IAƗ_Aj=>i;A2AA ᴵ@$f@വ@ {oA9A @[c@ വ@jtz@G]@RƤ@2GR!$@DE*?L@%?\~??/o@ꪁO@X9l@b@BNUV@E=A^cqAK! AXbZ@Dn3s@@3@/НA|ZA3YA.3gՉA>1AyJA_AwA:@۵BĒaRBjByCec}'C^0C%DCj(gC| CzHB~n5C^LC{wCp עMCNOCVSRBi7BNnBnBޯK5B"Ab3At'Alts@KJ@X {fF@$% ??zs>VY>Lu= d<,V-<ܝ/B;<-:8g8:[zD9@8u8l8O%'Uv7Xg6V_6hn6f ܮW69հB5!+4ՄS&d4Tn3 Vp{3 =rQ2ȳ9ղG2@1ńS,0tg0!I0P=.PN-W\0-.bW,.(,-XC+* 8e BV@ ZV@lalV@KhڃŔKHC2<%LI_L̤?9G29d[%':h:tIڨy;4;(]NN<,~_7> 9B%ԯJ&[J3E J.eI,3YI#k-H__qH~2견TH;B%Cfv9=AfΒBH@1 H@$M!G@:G@yYQG@W[1O@TW> O@[%XO@gN@7N@_vOO@ : xTUDT AJT_T&lscTsh|yT: xTNGsO@̯uO@}гY9O@UO@?o*RaO@~ZO@NGsO@ xxUdU򆹝U 4Uʲ#ywUxvaU>z9mU&T^STl?T\a4TZgO4T޴kcTiTgѨgT|Tln_U4ޫr>URJUxxU%%HoP@4P@^~KP@`KJGO@耑O@\][O@ؕQO@+!P@( kO@ѵO@hO@r.YحP@O>cO@z P@ 1P@Q{GP@@\RWP@LMP@豧jP@%%HoP@ jRsRnRSRWCKR+<3S1w-!OS(?SjRP@OnP@g?P@KQ@cE aQ@_ Q@F=DP@HQP@P@ #ݛsWByXDv#X*JXX艳XQX,XhVgX?X#ݛsWfFQ@J\_Q@Q kQ@̣ż wQ@z)/Q@2 ,#qmQ@YQ@hPFvbܣcJ^cQҷ&d!b#d:M 8bdǛGd4#'dRbE d&RCuNcxzce71P@|90|x2P@5EP@n^TBP@|fO@5XO@$t&^O@ 5WO@^OO@\O@0tQO@| 4 O@8FN@qeuN@[@N@\"<"N@S&M@ MxCM@,M@q7*M@UM@0֝UM@ M@xKJIIM@T7wM@0K6M@dM@o7NM@֝uM@Ԟ͘*M@L@ ěL@8i"L@l>>ւ5L@"L@K@yHK@l K@ީ>IK@3K@(MIXK@&K@8K@ `K@+K@nl@K@`ѝW;L@$p}L@b`xL@^(L@^M@lenM@ЖլM@@0bN@w]N@P|q hN@Ф͘LN@4\N@ [M@0ƌM@4`&!M@|؝SM@r7M@09M@R$VN@xAiy]b(]fffff\fa\y@ٔ+n\#Ed\L.+](\U]{,}]Gz\%v\Fx T\~/[@[Z= ףp}Z(\Z[Z YžvY- PSˮYwgEY$z>YPCY@Z}1Z|YZSrZ;M Z)t^[ؙBZ( [j&k@[ᷯz[9[j89\P\ͷ鲊\ \i>"TR@ <.-R@|гR@wEQ@qQ@$Q@|a2U0Q@aQ@M OQ@fffffQ@%zrQ@ѮBOQ@V/Q@ cZBQ@F ףp}Q@ 7JQ@RQQ@+y@Q@lIF"Q@ER(Q@R1Q@ALGQ@QKQ@ ףp=:Q@3.0Q@ڧGQ@!A`Q@geQ@(yuQ@rPL۟Q@/ ҌQ@镲Q@wg +R@ YDR@KOR@ ܺER@0R@҇.Q@:pΈ(R@Mg'=R@ZR@T F%=R@s)R@Y8GR@i>"TR@ q= ףPZfffffZ\(ZQXZ Zq= ףPZ)\hR@ffffffR@= ףp]R@q= ף0R@{GZR@)\hR@ (\SST_^5T8T@SS STSS(\ShdFR@@iR@?pR@ ]lR@RUR@b3R@@/R@ 8R@?6R@4R@hdFR@ IeUkѨtUjY# Vprn[VҚX!V|-xxV)$Vp`V0+VjϚUvIm}R>g+S myS.KS7OpmSXbRVgR`+6XRȕ>ɸQYqR &Q@&8Q`P<P-u٨PZ2QX0UP@ȫ)@P҂*PH}TPgnPdEüPO QJQ:s(PIP<敏OdOケ N̓@dOT(07P*ʜP!3QPHP0xN2QJق QHRʔRXRgR_(RtS $RS|eѨ/T$l g&TR#2TV&>h6UBuLqUeU|JR@$qsR@?lbR@(K$IHR@@\u R@iK>Q@ԄtQ@9ŰQ@FɕIQ@xۣQ@dT}Q@4YXoQ@BͲwQ@FD*Q@+tQ@&%@qQ@ZOA uIQ@ҧ3YC9Q@#Q@ϏqQ@2P@8<0|6P@P@اsP@Hw]P@@3TP@0SP@v $P@B`ҰP@WP@4ݠu+P@VKi P@@ O@KJO@ctO@PcTB*O@d 'N@%}#O@ %qO@crO@P4xO@AgKVO@$nlKO@M~P@>Q{z)P@FP@N%lP@R АP@ D՘P@R\RD[P@lc/?P@*4AP@;0,P@ƍlP@@`>P@[gLQ@U.Q@BKQ@d ͇Q@س Q@<@0|Q@ڦQ@@0|,Q@KQ@ȌmR@/R@NR@0&HGR@ -R@pR@eRUR@ dR/"R@|JR@ (\bYYQYYԕYKX{k`XGz.X(\"X!YnXHzGXQXX겘|XԕY(\bYףp= WR@N#--R@Q5R@q= ף R@g?Q@ƧQ@_vQ@ ףp=Q@p= #R@,d?R@Gz^R@q= ףpR@C5vhR@3KvR@ףp= WR@ P2*LW&S@Wl4rVtzW;WlVWM+Xa#(X:;W78WP2*LW:Sh1R@=R@.vR@fR@OR@Q1wR@ [R@~-xcR@lR@RV.R@-^R@z歪R@NR@ZmFR@ wgX[| XwW_YNz1Yi>"Y:7Y#28YjeX ףp=X"mXgoXXwgX+0du%S@/r])S@}˜.S@ZڊS@_LR@^I R@OGɫR@Ӈ.oR@R@s]R@]zkS@Gz.S@+0du%S@ Ͻ [i:;#[) 0e[fӟ[c]KD[3.[ٔ+%\kHc\1%]ŏ1wm]ަ?] .V[W2w\Bo\dF\l[yZbodmZMbxZ}ЖsZ׆qZϽ [w;S S@AJi+S@]h2S@ʾ+S@IR@R%R@wg S@FB[ΥS@;S S@JY8R@jR@fffffR@h:;R@<;R@_xR@fffffR@iWVR@JR@/L R@/ S@JR@w;S S@ FOȫWf/XG;GXWi4yW̛-88W+1W"E W$4pV~V V^kѨT@9 :T@ &T@W T@4 S@ZS@jMSS@1ZGS@g\8S@odS@@zǍS@aS@%S@oŏS@ A Qt䛪Q%75R5A}RSO`SfffffFT{GTQTU`UYQiUBO;VfffffVDUVk`V̌VvWV|a2UUfffffU_{fIvT(T= UZ/rU0BxUU0*FU KUUq= ףVQU3ı.ngVH3Mg_VfffffUb(U4)TR#T"-xS8LzS=yXS ϛS%P6S YxS72SH.RBS rRG?:SQxR ORQQ^Q(\PF|'f^P_LP 8PL7A`P5FjNNףp= OjMtP QWT@/T@wgT@h:;T@T@T[T@zGT@ףp= T@GzT@fffffT@ؙ,پT@hW!T@HT@= ףpT@ S@ekMS@D`S@ yS@yܝS@:zަS@l`q8S@ vS@ꭁS@jfLJS@S@33333S@DL$z'T@9T@ȓk`T@`T@FjnT@L7A`{T@L F%T@~:T@T@}tT@WT@ KToP}WP:pΈ(Q&3(Q\rK|\oKKoKπRtQJ &3(Q*w-!VQbodQD8ߥQ^ Q$Rı.nuR:fRn4@RRZӼRq]Q'XQ}QYNQX(Q Mg' Q KK[rPKFK\rK zi^[@F@[@ Qy[@֪9[@>UV[@fzՊ[@sG[@v}([@*4_)[@zi^[@p22@L2@xLA3@u(3@L4@^4@p*/3@-^3@ur2@p22@ Ԅe_@s_@4r,`@doH^I@PH@ceH@ 4r,`@_@Ԅe_@ceH@K&PH@doH^I@ O@{ Seuk~-V  U} *X@iPu@KhZ@@@@NM@R @0,@pMW@ Q1yQ*A*l\QP:L=ZQ⠲dQmRQQjK]\Q^D#'PQX(MQgE~sQJItQǑPܼzkP`̣Pۿ)P@PW1S?>񿀛3ǿ%DS?E@ژ?:wH?a?Sl?o?K? L@?W?G?2{ ? >C8QΫ)QoQ@iECQb >R@As.(R²&~ZRyKRPR!Ǒ RiLRmERB,R:PS5SxvVS(͆u'@9(@Xj(@xk3(@='@dv'@'lX;t&@[G&@'u >4&@ȂBׄ*&@0N<%@0,%J#@=rm"@]s"@—, G!@P4cLW!@ rJkxS^SSwgTS>I bSBLSS`_S$,HS`S d{S-]S$`ScmS\{kS7S+K$@C@%a@T@?C@@F@jY@a@씂 @@nT @ASA#@*0C?~D?A8ކC? *HYT׮c)TqTT2ʮTz`+UQUYd>Ű#U}pVU>)T\x6T+BPT 7OrT֖g%T~m VtTC>I!T6 T\xp=SdJSShgeɈSy_SIjrESplYpS&dȶS6uRĊR^$dHRjdRCĻRwR=FIRnt SÚX!Sq^ISvb(S(~]6@0 6@!LՐ6@w46@b5@B5@uur5@'6@Tkg#+6@Ԟ*6@ 6@c6@sԞ*16@:~ 6@\5@&5@eȓ5@i5S5@IIm4@n>P4@Íi4@-43@@ 3@{3@Bf3@j 4@)]H4@9`ģ4@,$.4@`NA5@v85@= 45@p)h5@\.]'F6@,¼S6@M2f6@6@'!7@W7@ PΧO.(@@f|Ha)@&(@k%@ħOK&@PΧO.(@{ffK@8b.K@͘BL@(K@p\۳K@{ffK@ vTQ!QJ  Q$.Qn>ժQ8QㆈQ|QPͫigQyJQ ,Q2.YQOg3Q4GPQ77O7NQ&d;qQ'|Q'Q,Q4geQFzVTQ HV 2@<I1@;1@;H2@DdHm2@-/2@>I>2@hm2@H7hva2@2l2@P]542@x2@TԞ2@3@.P3@ÍK3@S3@7hu3@dZ3@dI3@)3@ |']TB Te5;TYMT8P#TAdѨSt#S |S؜Sv[;uS5kL(S,5R9PRBR~b%= F<7ueb;N %h |Q0 ڛ@/2ewZS*˧uÿ 7Sj ߜSoTzgeVT">TyU%T@(;TVl 0T=T'T>#Sq|LS|']TA8ކC?e9t?+?_?1(ҿYzanU>Ip1W4{ */gB~b%= `Xnpc0f_Щ:wc~-6p&*y S W+<SMXOZݿ0}?gѿ"P?$?ql#?||@P@A[gnS@hMB@Xl0bxB@-& /B@\B@(j0b2A@pEq )B@"nVB@BVTB@T\[JVB@FtjOB@&t8B@eY>l%C@X ^C@:]C@D@ #VD@􂤼D@&?`xD@ 30D@{>@t#?@˖?@J. >@kq>@ O_=@r7>@ =@ Ŗ8=@XJ\)r<@P<@AN@"{-O@Z0boO@F͘O@SM P@up9@ xU8@ |7@0f2[6@W">5@p+YE4@ON%DQ@CZ1qQ@q#Q@oBͲtQ@Y1EQ@cKV)Q@iA2 9Q@~5Q@-LVWQ@NFQ@ C7@fFK9@P"e9@`8@Wq6@ :95@X۠t(5@[RY|5@–~R5@`Q~oJ6@(~R6@(ϸ"8@(SA:@H\<@2[<@ qP@AU2bP@s!GP@ 9P@zO@@saT:@xYv@\@{>Z2?ZǢ?C˵?Ռ?O?w?[ۿF񿀧԰ #f^0#'{֓6Y|6ȤpKi? hGQM@HFM@LFPM@L@5g2{dL@K3#L@ ӧK@ YK@0!VK@K@(V&>eK@YNK@ LK@<[`xJ@具ijJ@pǏ5ȳJ@0H\J@HkJ@8eҒ&J@5I@)gI@I@HAsI@zQI@TxQpI@HFH@xQ+I@O:I@D]3YI@]7@I@2>2cI@@bI@`U` I@"CI@k J@x͘^J@:9 wJ@H EJ@wHp;K@&OK@[}K@O+B?pMW@lc@P @/:H_@`"%!@ 'B.٭-?\Mb-I(-w³,8%,^+;~*hBװ&@Os%@yP%@(ym$@rj $@⪷#@@x O"@`4c\!@ D17@xՃ7@P7q)8@Km8@_AH=*:@(=J:@ ׹9@`9@|cjg9@7q%?8@D17@؜c=A@x֣A@1`A@@)(uA@A@cfA@|) A@ JvPA@.AfA@p\[A@؜c=A@ aG9#JsGmBFjLG|~H˾+1II2J4KdL9ߡ($NO;5YSOoB!ONbX9O@PxQ)ǺTPQ|mP=,ԚWQ+H3JR^I SRx]QHmPyܝ1QrQjQj0 G QJP\r)-BҌEAݵ|g@;pΈ?0Bx>S;=~9a_YY6tv287L\: q39Q97ܘN8ŬC97N5!6oeq7B 3m8,+MJAG8GL6 'P6F7N ^,6nض(4p>o4J5,yp_3^/ 4KqU3!3 5`4镲 y2Jvl39z3fffff2471]wb 40S c I0_)j( pU)N/674k+7|?5^6![=6G ^/8N;,g~e?fffff?겘|:6]zk4a;W&R?AHĔHOCVCC9EaG#0T@W[ɌT@HjT@T@ %T@O@L1O@מYN@mN@Z*oG8mN@:N@# N@DL$zN@QN@3VWO@h?O@[_$P@5P@wW]P@'KlP@@|P@{/L~P@r3܀P@LP@ ܺQ@5A}Q@BQ@ -PQ@IQ@ƋQ@vR~Q@ %Q@s]Q@wnжQ@/$Q@+yQ@Q@hW!R@H.!R@yC&R@Ral! R@F(R@=~oӟSR@ͪSR@d]R@'KtR@5 SR@4FR@9EGrR@>:uR@VFS@|a2U0(S@ p\@B6Y'\@!㜰[@bX[@z&'[@7V1}b:*Pkg&]jk0gx k;pH > a Hb+ɚ x 9= p[T%![[ڽ 8 *p`iy`VY7 j7V j0`@јC`@'`@'nJ`@c&aۛ`@gOj`@j0`@n݋ɉHIuۚ!2n݋ _@ L#_@JԄ _@SL_@:wiË_@_@?"S8J* ܺ !Wk ( ?"S }vZ`@@O`@y6+`@YgY`@r1o_@OZ`@$`@MW?`@}vZ`@˩&?@+jЃ˿D% hm C! ߍ< ˩& $ ?a@5e}a@cea@ Ja@ .a@%b a@Y`@u)`@7dx`@_;`@W)`@[{`@5d8P`@Ѻ]+^`@Tz`@Io`@O]G`@t|`@#`@!wz`@u `@;8`@AI|`@Ԩ<ī`@}#Q6`@a4bD`@`@ݰ=a@UZ Ma@XeUa@sL@Aa@^1ũ3a@1\5\a@ da@)aۖa@EIPa@ ҔټE i7'4>K#A+$t^( 8JJ'y"y n{&׿7A#`B%H8 <@!ar@1}8J~ e^ c o+;s7 jaSeO  `2/lΒ`Fdp&G9;d@w T ,1 Z[& @Clp\<" -*9D_@Rx _@@]^@u j^@s8^@H^@J*]@h|]@2]@~r #]@J ]@W]@j]@:la{]@J6]@0Nܐ^@­^@Ϊb^@Όݞ;>^@{9^@;%g^@R^S_^@B'w8?o^@[k ^@U!^@2 9^@c^@*~ ^@FJf^@?¶^@R|̍`^@>τݘ^@^^@>(^@[^@`s^^@;^@s^@*\G ^@laC^@TpF^@[P^@!c_@^5N\dO_@*9D_@?#J?"H3X?Nm?9?3ކ?6" ?6?@3}GFD-|~jk  C! ?q"9Ϗ m9Pcs@ai OXp}WLd4pg#O2!C҉>G\7 Հj< 5 } &x\ EDX`t1+8\="ã=A !^?we?N?$'?pgb?c:??#J? /dY`@4"`@OZ`@  "`@;_@Vn_@J?_@b _@T3`@E( `@nF_@C'n`@/dY`@DK"?A@?)?@v?`1e@?0?ѿQ֏p\"пO:w?DK"?  @ӯx?!?~ hݿth Ah|i|xxo9eB!`T2 (@aI[k cv3 j@mٜA ! W>@!nuθ;?=\?f)?a*>?'{0M@` @TDڌ@ #uTtZ@dvZ@u1Z@ʼgZ@;8Z@"Z@Z@b°Z@laY@'LJP{B"L~A?ú?Ot,?<{a?1Ҍi@r`@$Ye @: @ @|b;@@e)V@ ¸@`l@74gx @`'@s;-?@?cr? P.q?x_*f+eb `ۂ B5&$|Q>~h 8e BV@|/p~8V@(J!V@,V@~,gV@sV@;V@y/m V@@% $V@ NܹVV@^GuV@RzV@LҷV@,V@n~W@lV@IV@[2t,V@!6V@-V@S=GX W@N +W@XB6@g6@*7@\;8@hjl8@_ da8@7=9@ *9@@fOr:@p1:@ Ԟ9@LμSE9@H!9@)L%9@lY8@@4b!8@[8@c#7@/B:6@*՟7@7@u> 6@ oTX@!X@JSX%X@Nx X@B3GW@ր/9W@*ItZW@.3 W@CV@7LB<@(ܼS9i<@i<@qs=@R=@0.G=@lI<@寙;@ 2ߐ;@ *GT@ӷ*S@2e JS@"o/WS@XES@T{^S@I/S@rv(bS@*<˘uS@hj.>@@+>@@k?@ܜ O@@`3=@@:RH@@0h@@55)A@L;A@ +He'N Q@<_VQ@Vp~FiQ@sJQ@@pQ@97Q@JZ(R@$9X34R@jlj4R@· GR@؋R0bR@R1hR@b8V@8e BV@Ԟ$7@TԞ6@#̓es6@;6@񌽘4@{4@> o#[5@\-dk4@+4N53@ `Ķ1@6/@X|~<-@03+@S8{)@qX'@ ӝ&@(i$@!'m!@0F%@ :ڃ @XN!@nn"@YZ"#@0%"$@ $@A(@љ4*@O%+@,E.@a/@莽h/@jO0@dY0@BB1@T*ի1@PM2@Q#z3@ _&4@kkI4@ ~5@Xzx5@ٟ7hʰ5@XB6@ dI I`#p"U"@r D#Пw5!'?!4tV,3ؐK@~UK@O}J@)(nJ@ީI@6ީI@P&K!J@0⚓J@X^ J@ xs-6/*0pr1$+3dT4dG"6!7X`:̀S8=i8:6XZ5/6}7HJ5ˮ!R6'{s3X6)2[J`1-6{.8+.jz-xs-HV-P@P@JP@ԳP@nP@g]REP@!u͐P@;ޠgP@m:XP@[RpEP@/6E9P@MCP@$O@3VbO@:XzO@@O@'[RMP@'`HP@["sP@HV-P@ *O] R.@#xn /@3u…-@`5x{+@ xR$)@𤮉(@`@qI+@ ,@@g13.@$.@*O] R.@{ָB@~}C@AsjC@LyC@4A-C@ʿ$uB@ ӖHaB@XB@daY[OB@DX.B@{ָB@ `7#@Ыk"@yk!@OQ @J @P3uJ @0Ы&!@Um"@p&V#@`7#@KJ@D@3D@)2sD@l>֙yD@~}l0D@|cC@P tC@0ǞC@t-EC@KJ@D@ 3b9SVtdSsS[;S_Y1SWqS.^44MSį9S<4 S΂ bS3b9S,^5f2@p<}2@H0.32@DB\t2@ʤ92@21@pMZ|1@ܕqD1@<# 1@{#)2@,^5f2@ n`@o`@eƯ`@c3u`@L`@9`@y`@`@ _`@4b`@٫L-`@n`@A@D.A@ >V@@ݷA@ X@@~@@ xC/Z@@@@@i@@ g2@@5@@A@ %#>a@>#a@YyNa@A'ĤAa@VZΫa@dȉa@#&ĤD|a@ca@O)ama@zzq[a@,a@ʞ)a@ǯc`@'E3w`@{wÓ`@'%aK|`@5[1E\`@oSK`@+PZ!-`@I :`@y ]SN`@?9zF`@ٳ#QU`@7j`@CL`@.8_`@VD`@)`@`@`@u``@}+&a@ 7_a@a@7Řa@AEIP1a@#>a@x~}/B@ީEC@D&C@XFC@`ttD@t"D@(HD@4*C@ltC@lRB@iB@: B@(U!yA@tA@mlyA@\ _A@RA@kjT@@)(@@N@@$&(@@Ԟk?@hz?@8k oJs?@uA2@@U`@@<\@@p\Y0A@dc[LA@@]@@Xr~@@ƚMA@sUA@A@(:eA@Pt,B@x~}/B@ }0b@ a@'da@#hka@4a@t-a@a@ݸ#Q)za@:~a@75'a@#gUa@7a@#b@ YZaa1b@ E3D*b@}0b@5E@4OHF@mlSAF@PF@bF@űE@_HE@V@)HE@5D@?`D@L7VE@xg2cD@~E@M7E@x">1F@5E@ xr\5DK@옞K@x@ٔcL@E.M@أN6M@`b[I@dz1OI@ I@[[%I@4 EI@ أN6M@N.M@8P:cL@2K@xr\5DK@4 EI@%I@I@*iOI@`b[I@ PG1}N@@N@ mKP@J@K@9>VW-K@  mKP@P:N@PG1}N@9>VW-K@0FK@J@  mKP@+jP@xqb Q@9>VW-K@@\LK@5|K@ xqb Q@ jP@ mKP@5|K@(QLK@9>VW-K@ R>[R@:䠘R@9S@@|J@H.J@>K@ 9S@LR@R>[R@>K@xS&J@@|J@ q= ףA@(D!T=A@8LLA@}RA@ o_VA@ףp= A@Pw`@@Kά@@@Biq @u<n@ʉvR~? ?{Gz?_q?ffffff Kά@@K A@hnB@y)B@nLOXC@ffffffXOp{j ۧ1 nLOXC@ mC@@^>@ܝ?@|г?@  1*9@ 8@= ףp7@P7@[<7@P6@a5@&ǝ4@'K"4@|%3@=yX 4@I3@ <3@E|'f2@[ 0@˜.m/@=!7}.@9]+@B!**@28J^S)@9YB&@|г?@Tt"@@"@@j@@KQ@@]Ck@@ h"lxZ@@lV}@@u7Ou?@xqZ>@-#>@@"2D>@4Ry>@;5Y.?@JR`?@ni5$!@@Z![@@ {,}p@@!vye@@ @@ .mrT@yST@Y15T@6u T@Pw(}S@S@U{KT@MT@ѥ!hT@.mrT@Л@p9 !@8٨=o"@`"#@(f @4\ @@V/Y@ q@pQ{V@Л@ #H5@P 36@ȾD7@U8@:=9@Ou9@4H~:@%jOL@N=+L@Vީ#L@ѝ/L@%WL@҈ L@rlһK@ "P8@c"8@(`"Q7@(:;6@i35@84&5@#H5@ROL@hIL@ \[̀L@nL@~H-L@UdL@%jOL@ @@::11$}-B-`ڼ Gp% @9e. ? \E}_'mY)*s5:%XL%[ ;@4t^<@ SPH=@x|P=@l>@mqM>@Ԟ>@HO%?@Ox?@"֒CA@xA@ L%[F" ~ڼ0]&jP܁4{qwP)P!l"w8""#.!#`[$TK*krG*'0+m?,-Q7-`$-.`k:+.i/@NKݏS0%M C0@쁭і0f&0@@::1xA@ZA@zAs.A@,TA@pjϩA@8KJGA@ȽA@l$A@ml9@@\[@@$iGH@@W@@@t-?@=@xV<=@ F<@P &<@ <@8v;@8?#;@:#o:@pv-!A:@ؾB9@9@/8@0.[8@;.7@Hy7@16@0 (6@,]'5@l5@ 1yH@@-UH@p~WtnH@8*lH@H%%H@$E@4BPE@?)E@F@'\Md \y \lv[8r#ļ[|Zq[X0|S[d׉v[[[ .Z[ Zeq[0|[R[7p[n8[<8[y]t[@vH.>@`wm=@*G=@r<@"p<@ѽ;@V;@Ԟ;@`$;@Ij:@Ԟ:@Χ:@#;@hI;@pFl<@ ˫BJi<@@_ o9<@<@`=@ &W=@@Hy)>@>@7hd?@0#?@'?@`N?@k+?@1;q>@0Og>@0OD=@P T<@w<@ w#;@q ;@`G]);@:@@ dq:@+9@Ң9@`,9@pa8@@PE܌7@Px>6@urD6@pk5@l5@07h5@ ֤ 4@O4@L!o4@&W_3@p!]'P3@d2@J2@%41@1@&1@pRP+1@*Ǵ0@@&n80@PMx0@`/@ N/@@/@`u 0@s30@s6]/@6h;/@PX-@ #:3V pV\U4U̕-U0,*[U[ׇUT+*[#UaU`+RUt6#Uô"Vh |fVԑVTiVw V&VPfVVcW4O2WOcWl'?W)S7W,MuWlx6Xȉ!X% LXxXXK"lXt7*wX\`qX7lXlaXLgHXRaXq2@ˤC2@LB2@0E 3@6 ox3@pH3@`f5bA4@`!#4@P*]'T5@ C%5@ංvu5@ a~5@uC5@4@']' 4@ 3@H3@\gF2@6^2@[2@`$l2@q|$2@A?2@d2@]'9@ףp= 9@ V8W@WW@3hPX@œ9.ՎX@U.I@hH@b4H@n:96I@ œ9.ՎX@laPX@§W@V8W@n:96I@:H@,H@U.I@ J?8Y@Wf,Y@Ws`Y@0CLY@KT(I@DI@RZAI@HÝ I@ b }Y@nhięY@^yzՖ Y@56#Y@,Y@QY@w8YY@z(Y@!BY@ NؤZ@R(Z@oY@F(Y@ƃ{Y@FW|AY@yݞY@eY@ZY@jY@A 5 Y@a(@ )@e?@@[@a) @+%@ My? ?+S?]'? Y@JҌT@+ @ABvF @@<@S@jk@МJA@90H@@^̏@ Bsx]@bI6]@g|$]@s]@[P]@*1@]@2l]@DZ-i]@Z=KH]@Th.]@ݞ ]@ p\@TDڌ@!@q(@>@@Q/Y@s0@`fL@Y~S@VH@¸@py@  p\@Lp\@<\@fӦ\@6\@&s\@py@,'@=t;5?70?Ҟ?@;> @ fPX0@@_0@>{ 81@(3c1@f:1@bw2@acj3@O 3@ Q3@b<:!hC2@Ы^k,@,@x*@BϠ)@ЩPn(@dAw'@(21 b1>11@1XO1LfdZ1xCbl1 V0$~0&L1 HM1 dAw'@'@97)@djEX)@1)z*@Ы+@ ,@7|,@qjE,@Y|-@(O]-@`c k.@Pq!4/@fPX0@ HM1u2 3ɗT3֒@r465y#x6=g6Q _7Dgd9VJ :jH;sB=;b< Ld@Aed@K#d@& d@d@,7`d@؍d@> h.d@uId@l ]d@4"d@dZ!3d@Ld@4{5 ?4u4,~462 4q4ɗZ&55z4!69df6I%'(6t\n454{5 )~.@M(D+@3u+@Ф0@e^/@!,@ 3u+@0`n+@)~.@!,@q6e^/@Ф0@ _t\,@m+@1,*@`+*@@,7(@7<'@~%@ff%@`:$@N] #@S{V"@@7@R&CR@!H@+@\@ /y@ lT0%@o4&@`"y"&@b~B'@G;G'@ h~F(@љ(@()@|D A*@ߢ{"+@`u +@8ٯw,@6^1@8wVv2@mA3@?4@ҏcC7@D9@4KRh;@@Yr9@wT@ڦ*T@Z7T@ .H&T@shT@.HGS@lv)BS@LoFS@zʙS@e1$S@&T@wT@ |馠e@Ye@Ku{e@[~e@;re@証de@6$Pe@証^5e@lae@Y2'n e@kN|d@ yJd@c d@јpd@}n) e@Ut*e@y:e@{tSe@wyee@#]~ne@Wd߉e@BHe@_–e@ e@!T e@YTe@i:e@Ae@kWe@|馠e@,uDzX&:?D*:azD<3gDNf:D'AE~\E^)EnQEYFpcR3Fn%FTA&GC0%%G$YZOGRGz-Gh WFNOrF@ FEH\:E^,Ei7*|E7E:D'D'vDĻ~D,uD $-(e@]e@%M@D9nM@8%M@ٛM@$'/M@hnM@D͜M@h^M@V]M@M@T?UL@-lL@ f2L@7I0@xca)0@b؃ 1@p`0@, ou:1@'ߡ1@`;V1@(wE1@hjxM2@P*2@Y2@u|2@XBIL3@j3@x7h5>4@{|?{4@ ;m4@,]15@xZ#o5@ 5@Í~O6@94˚6@%6@Ԟ 6@Xjϐ7@dax7@l-7@ YI=8@A?8@ He'N Q@%5Q@"}rQ@D̍Q@ Q@NϊQ@P[`Q@JtQ@*1FQ@|+X4R@bB\R@R@R@TR@F-XR@LJR@je'oR@.O{_R@(lsR@I7S@*<˘uS@Ԟ$7@0.[8@7]'E[8@gV79@9@@3L}:@u :@;;@0.;@I7*<@1;=@p#>@tP?@Dc"@@:a@@g2@@$\[(A@Y_A@M@A@t&TSA@L;A@ *<˘uS@CW S@MifR@B R@L;A@j0A@$OYUB@")(B@ B R@_mۤR@KiRR@i:R@6)Q@<[PQ@[Q@N?@ y(>@ѫBP=@qx=@*^=@yV?W=@w=@XQ=@<]I=@ _oN@EN@N@lN<)]O@Tɱ`O@r\O@(FcO@ȹ_N@[yN@<]I=@CM=@Y<@7LxB<@_ a;@d7;@l:@<}gn=:@&W9@ [yN@IsO@0X3!P@ZM@hܗP@jNP@eP@He'N Q@&W9@{79@r-<9@Ll9@}k8@F7@Ԟ$7@ xvVS69uYnS%QS&S{SV4[S&S͈SVf!T喠PSqECS SęЧS׫ޛSjtSL^wS[pESrJkxS 6s @`% @H @ V @ Y @7 @H6@@w̏t@UE@ݠ@2@Cԭ@f)0@'<. @,8ɘ @X4c @XXE+!@A`!@`_1!@o!@#R @ k @LZ" @t̏ @+K$@ >D¶_@_@2@_@J#`Z_@iD+^_@@h0_@{~&_@Qy^@jF6^@J?P^@^^@վz^@>x^@RUմ^@ʒz^@V)w ^@)_@օ^@r>%_@}r`Y_@@k_@fMZIW_@"Xp5u_@rG_헌_@vf'e_@22_@>D¶_@04cT @Xdk3!@؊Ux"@XљJ#@W!@8/ɺ!@p?!@X1 { @ ×,b!@ @`* @2@`KG@KpZ@ ,@90U@'NCq@p`@0`:@R@ JA2@f\%@pcX,@s@ v@P\@04cT @ ZB^@_@ ^@f'^@ _^@^@V{^@R^@ڕ^@Jᕽ^@;^@ZB^@xOI$@h'w&@*$@%@A%@3a\$@#K#@C=m#@N\ "@"@OGr#@xOI$@ y K]@+]@ #]@FZ]@(0]@"1Ø]@=ˆj]@[P'K]@y K]@0"@hyB$@0%@jZ"E&@dX$@v^#@ר=?""@( @0"@  x^@t^@ny/W}^@Ji*^@ LШ^@Bw t^@Jw^@^C_^@ x^@X'@ʆ&@¶%@$@?Ɍ{%@X|DT&@1*'@(w*'@X'@ )`_@8N_@nݞ_@Wp08_@LZ9_@\_@Vua_@ 0_@2'Q3_@F,Q_@B_@Z @_@PK*@`]*@?h)@80?i(@#R#*@ !|^@N[PT^@~i-^@­^@*[U^@ݕ]@Qp~]@J^@2$^@ݞ_,^@by?^@C{+^@ ;(^@H^@BoX7^@n{^@ŭo^@%^@:-o_@"; _@:7^@J`G^@oX^@;^@u n^@ndS`^@Ukj^@2N%^@.Qy^@d'^@oX^@ZM6^@|^@ؚ 72@xca 2@$V2@hca]1@`' o0@ #]0@@L .@H4-@H,@Uj-@8xG -@8*Š,@X8y+@ E+@;+@F'%_*@N+@*@@#R)@S)@zZ"y*@drms+@0Z"O,@k3o,@! ,@?.@PōU/@`zÍ/C0@CV1@8n1@x92@uz2@ؚ 72@  3sc@[ϗ+)|c@ec@F#qc@Brlbc@aQXc@nOTc@usPc@uUWc@VXec@ 3sc@7 `$k@G`i(Lsx eD$\as+1t?r~9$7  KIsb@_2 c@oj c@#ј`c@)6b@Ypb@}_b@)f1b@y98b@[Zxb@5Hb@^b@ub@یb@+4b@[oi{b@Vb@4b@('n$b@Qb@C"b@KIsb@ `iY/|x@Cz@`iE\Pի |Q>pnktOX|Q `iaC OXU0hCCЈ Dp2aEp!BU\\(= `i  ?a@'󺋠a@EIPa@ 0v4po@Clp\<" EIPa@ݯL-/a@;a@7J>a@M)a@%a@ ?9b@-(Ab@ό,Rb@xߌUdb@gkkr7}b@M^Wb@9b@јb@ф0b@ǜb@ݨ|0D^\ qtvDgmp즬A#Wј>{Ҿ C ైF X!G"hMB@>VB@DB@ C@~o0b/C@mlكC@ؖȕC@ xCC@.~}D@ҭZ*D@n7D@@5T߰D@@ND@]D@ bD@<;D@LHe#E@=E@QqD@ G" ,H!,!-!g!(Rv"_"O #Jr."ha'!@nD~!(p6!y @ %l`XnQqD@D@;AD@x0\aD@Ͽ$dD@ZC@,:9/C@bd^C@"-C@VxC"C@hKJ_B@Xȿ$5oB@bK}B@l0bLkB@hMB@ }za@FIPa@Xa@Da@a@Uy|Ca@ela@}Sj׵a@Ǘa@;rla@?yXa@Q85a@Yga@oa@ka@ )a@:+a@^a@;a@#ѐa@Jb@}za@^_I@Y>I@<^J@͘/J@t_..K@beK@Z`J@7>֦J@|LTI@\[yI@ީH@nmH@`TeG@fH(gG@8 F@0b^G@yG@h:OkG@HG@Ү=H@^|H@^_I@ bX9e1fffD4mfG|fnUfT:X\f-[닄KfD 'fB!fQyeiƢex( eh\8e(\‘eeL;\eW[P@$0YP@wP@~k,P@)t^coP@GXP@KaP@]hVP@{Fw;P@fffff(P@`vOP@,P@W}P@%\P@j>"bP@~P@moP@P@!vyP@ڊeP@$@M-P@ f@>yX5Wf@u\f@f@f@jBvQ@6W@#pX@ %X@?o*RaS@YS@:p/T@ǺPT@ AT@9 {T@z3 T@KS@6!S@OeS@?o*RaS@  d@tEd@|BVd@}|BV d@ ~c@)Szc@Er.c@ d@MX#˜#se6g8#"c|"wmG#.#MX# cvi5d@n(d@Wpd@|BVd@A^9d@#0d@cvi5d@xN03#^T%r=" ',أ ',أ H>9!X#xN03# i?9c@)Szc@uEs=c@c@ᨼc@9qc@hGc@4a[[c@i?9c@H 9= @ uq=]̲G >Ggs@^u: #!H 9= ;~*C?*(j)=/(yj'³&`4c\!@@(S @901@0@ @pzݠp@Plc$@ wg G@z6>W{F@*t^cE@@[닄bE@#JvE@p"\sD@K7 @\@#@0@ Tƿ@3~Y@ p"\sD@ }D@vD@I2D@3~Y@vkF@{w;O I2D@yX5D@S?o*RE@ZwgE@x#F@0F@c AJHG@4G@6LH@sH@P I@Q,FI@}ijI@DԷ̅I@PI@0"I@X5;I@KԲ]I@m!I@kCH@EH@^yH@;OiR $Ry;il g?#Ed?=;k]@HĔH@8d@zަ?[@7@D4) @h?e"@u7$@Gx $H%@x@U&@;M '@X4 (@z6 (@yZ['@Oe('@6T&@^!m<&@ C7@W~ݐ7@7@0mA5@p+YE4@ qP@qXP@9P@Otfz'Q@NFQ@ p+YE4@fx4@`CAH3@8)n1@ӧ1@:0@PG0@@O7.@O]+@`\+@`0$+@2H()@s'@U0'@f%C)@sə(@0i{V&@B&@NFQ@$U;+DQ@DmxQ@q#P$Q@\/Q@JVQ@fn[P@LPhP@L@j^2P@5ŁP@kc$P@<P@,ѵlO@P!FrN@gN@nlN@DީXM@HMmM@ B&@`-m'@- @)@-)@?F3,@BWU-@Bs/@Hwr0@ xE0@`ޱ1@8L"2@`w1@&1@f:1@ؔfd3@)^5@h 65@Ht.6@C7@HMmM@zvL@@OOgN'L@F5iMK@+2K@8L@DxY L@\[CL@KJ#\M@֝zM@қ+| N@TE{QN@QN@v4W_O@{O@ܠ9xP@!`AP@NQnP@ qP@ pNZ@pbZ@ަβeZ@D!c1Z@^u-Z@^6Y@~}Y@ =Y@@nY@TEY@_CY@)XXLBY@ RY@&Y@L #Y@da/WlY@j,@8lrmr-@Iw$/@4cxq0@qOm1@*=2@4 DO2@ܒԞ11@HU1@ L2@H:31@Th2@kv3@=E"3@;4@Ӗ1j4@ 4_mX@X镐X@ؕX@RCX@>X@??X@4ئAX@OF4X@b }Y@Y#@!@,4 @ }t-@큽 @A_ȡ@`_@!d@a(@ A 5 Y@ gY@YAY@FJdY@Y@rNX@r7;X@fq@hX@bEGX@vZ2Y@[PBY@4@ 5>Y@^<5Y@dkY@4oY@@^̏@+e@2nm@!@PAG @(lj"@8z"@4c#@آdV%@=/(@x{O*@AP*@0|DA)@ *pJ)@__(@ xP@J@d˛GhJ@ XuJ@caJ@7UK@@P^K@GJ@@&uJ@.XJ@̭J@0(+{J@|&DJ@P:%J@J@CCD@LSD@"qD@eE@D@\ۺyD@PD@1pD@PlMD@?.C@R1C@=.yC@-EB@H)(vB@ = ףpNףp= NGzNN(\NHzwN(\rN= ףpNHz%@Q%@{G$@Gz.$@$@Q8$@(\µ%@Hz%@ @ @Ыo @lB @w.H @3@h7TXs@f @pڠ @~w"@rc "@^$yB@Wr7B@8lA@Z>SA@<%v A@] @@@R_@@@@@% @@ gN>@ 9YB&@Ϧ"d7&@pB%@$@>jL$@ 0H#%@U%@!B/%@33%@963&@Њ:&@pk0~\$@qk$@p#@@ @ @@J*A@@tf@@42.@@,V*A@b]jA@ppjOxA@\EA@,k0z4B@<3sB@ Gq ʋB@ -E\B@ gpB@(̬B@^$yB@ d=K^@ iq^@ض9.|^@]ή_^@\Gu,^@=^@_^@f'/^@d=K^@Ԟu6@d8@urb8@G1K9@y׉8@C;g7@)#6@o0]w5@Ԟu6@ B%ԯJEJNӚJ*PJȽFIK$ IK#eGK0Hi|LI_L3Z@`Eۙ@)$]@52 @feL~?yzʗ>sB>g>,~_7> I_LLCM|;MPM=,M]6M,~_7>N=?FK@rK1@W,-@i@ ]6MլL#E2L+ڎL}HKtyuwK.8JB%ԯJi@R4;A)07A?ynApW`AMRyA3m2A3Z@ XqcBgc]3fZcMYc+ac)D/gc;Tlc2w-!yc{c}i}cz3{c82cYcY}ck }cԂ}vcXqc^3@v28J^=3@Nw( t3@%:3@]C3@ F63@#~j4@䃞ͪ?4@*gD4@#,4@5l/3@ƿϸp3@ 73@DV3@.43@m2@^3@ H- PSPPP2P7QR9Q.OQ˾+QQQ ףp=QݰmQRQ#-RQUBVRsh|Ry]RS Sup{4Sj9oSƱSfSq= ףS{GSjSP.T=uQTH T7;l*TghTԅTHzTTQT!ψT\a:靕T>uYBTRDT T }nT0sLTUpq| U $(~UmV}&U"U 1UY8Ur-QiUXAU.%U3VzwQVffffffVQV)\Vףp= 'WÁ,`hW7W)\WGw;SWN`W1.W5YNX)YmZ33333Z33333[@\a]S:XA]^(\µ^BF@F@|~G@LTo lG@HzG@bGPG@[닄XG@QF@{GF@,F@q= ףF@镲 F@~RF@&F@hohF@#S F@́l\F@|E@8׿E@"E@E@(\E@Q{E@ThъnE@X .E@]E@iD@iD@! D@ 3D@ ףp= E@ףp= 7E@= ףp}E@(KeE@Q8F@ml{F@Z\(Yףp= YQY7djYQI=YZ {,Yףp= YzGX33333XzGX(\XRaXhD@@2bN@@M(D\@@`?@-9(?@ cf?@vlu?@M~ޤ>@,)>@Þvk=@QE=@Q<@(\=@m4=@B5va=@4<@(<@ ףp=;@ףp= :@Q^:@(\:@ףp= 9@ iRaX\HXQUXQXXHzWX\HX!Y&X5FjW\(WnQfLvW겘|NWyZW'>V=UV>:uVR8qVϸp ZV&NMVQ[Vz]zZV|TfVS%KVXVkUUh"lxzqUÞvkRUI)FUfffffUe,iTQTTdpT1w-!T(\T0 ףpmTQUTE*-KTQ+T2bT]zkT TVTD!TNG"TQ>TgTTzS c_T3}ƅUTl[7THTwJSKvlS\"lxzSvS/rYSSW>S72RR.1R72SsMSH \?S"S/n"SfffffS WS46R$SS-s,R'Nw(RvR<_RGzR):R R R72RjR/}RmRr|R+~RGzVRGzQOupR= ףpmRm8R{GṚp= Q0BxQ)\Q+j0 }Q;5YxQp= ׋QQQHzQ̴Qb('Q^yOQKToQp= CQQKP- PSPףp= 9@Q9@)\5:@q= ף:@zGa;@Gz;@,`N<@0 <@Gz=@qt=@q= ף=@ec=@d;O=@Oc&=@y=@HKN=@Tr3(=@ 8J=@wӂ}=@iƢ=@T](>@0P>@:pΈb>@O}:F>@fffff>@ r'>@!q=@Yڢ=@p= >@je=@=@̌<@4h;@;6~;@G:@Q9@p= ף9@X}ƅ39@Gz9@M49@0*9@T4:@{G:@(ףp= <@ux<@Gz.=@Z >@Xh>@ 7p?@xz,C@@m2A@@F@@9@@8@@@ u@@˜.AA@=~gA@L~A@(\FB@&jjrB@lm{B@lC8B@ C@QC C@L[C@33333C@B (C@[ AB@DؠB@+j0 ÛB@gaO;C@|C43C@܄ҡdC@{GzC@+οC@X5͟C@w#C@UBxC@4.C@dp6D@*D@(\D@)\D@9#JD@̼D@[ɎD@TKD@(\E@p= D@ףp= D@{G*E@I&nE@"E@,jME@= ףpE@aTR')F@4?gF@BF@ ʒ3 c*.Gc0cV3[Q'cБh8cz&xUc_Pc,!)@cʒ3 c8!ӎL@jϧL@c`aL@)(L@hRL@zvL@heL@ ^L@8!ӎL@ A픵d .'d95dh%d7M,+dRWdA픵d @%N@hQz,1N@EN@4M@RM@AszM@ @%N@ .PyezNlqe{Ue|2F"@X$"@y6#@x^$@8 ]%@пw&@u z'@(͆u'@ &f'6[@ީdZ@^F 5NZ@VmZ@ʉQysZ@miUZ@NN؄Z@"IY@N\?Y@DkY@`h5@j5@F76@``#U6@Ԟ6@ Z7@`ҳ6@d)6@Xs6@}xv6@ DkY@`w8PY@NP Y@s Z@ 4Z@ub Z@<`Y@ԏ FZ@U?{Z@*MGZ@_Z@9.!Z@s~Z@}xv6@54լ5@kk=4@&W<4@)3@0A?3@C3@iؾ2@`>=|1@HZ0@^'+/@p =g.@ Ug,@ s~Z@@TZ@h%sZ@"|ݞsZ@0 Z@ x@hLZ@n̕eZ@ Ug,@8<1+@pA(@4c"'@љr%@x;S%@'A$@ n̕eZ@2Y|DZ@2Z@dAp JZ@^Z@v#Z@Ϳn[@^L[@ WuU[@"8[@l{@h?[@ݷJ?*Z@\PZ@L^jZ@q{mxZ@íZ@&f'6[@'A$@H4cD#@ _i{"@z3!@P ?:#@HI$@HљB&@CnU'@(4c *@p5c.@Pgi0@ 0@L:2@`|3@ca3@d4@`h5@ dZard@!d@먞d@qߌad@ d@<d@dZard@+*-pp9@-zlp.Л[nV/!:/v2z/+*- 1J@IMdJ@4c5RJ@7I0@AY1@1n83@ 4c5RJ@<.H@|wH@G@?)G@o_G@\|.G@Xb,3F@ˢP:F@1F@+{PE@'޽E@qWtҎE@LE@\?)cE@1n83@JVݝ2@,qժ*2@b1@]'30@c؈H1@~Í;1@PtrUU1@0n1@jF i1@VgQ1@\'z1@Ť1@L0@X7hY0@ \?)cE@: niE@O+{YE@Uo gE@@wlME@{.5rE@pVoAE@rWt/E@l; E@yѽE@`~.jF@GN?F@o~F@C$AzF@ TF@P F@_ TF@?)[G@^G@|*U.G@ CӕH@4wVH@CěH@;vI@HmJ@yJ@mN1J@1J@X7hY0@/@:p/@.@(~G:m.@j-@@ ,@+@Ȏj q*@y-y F)@,)@@˯w|q)@A0f)@hљj)@1* *@*@,*@_*@?brm7/+@(pI,@Xk+@],@ Ftj-@UY.@XN1/@$Nz/@0mOa0@7I0@ 1!2IӃLcIkpJ.w)JD&4Jlc@¸XC@@FX @ V̏@ 8]D_@N6^@zU^@KJ@0|J@8BJ@ zU^@J)^@8]D_@8BJ@гYJ@KJ@ |n(;VY1.V]Q#Vt VVa{ECTV"Sw^VDu٩ VSACV6VeV>;,V:3V H/@ i|;0@@{C0@Ŀ0@4/; 1@ {!1@BN}1@dÍ1@)2@h{BY2@ LpZ2@q2@  eQDU4rjFQOZ=Q@jMQmu0QJ]QAP7LPڨ{1?_B2D2O2~tg37Nf_4l~5b2w6貋e6 9vXLM|I_IL LzyuLL$ʿLZkLb#M{1MJMdFNEN C% Nr!NXl:N]Cl@NfJM|ĊNz0eGJN/DOF˭fO?@\ Bq&Nٸ#Շf#0f$w,$P7c4M%P~ & w5U'@-?' lW(}8@ 8@H 4@9S0@,寬30@{/@.@hK9.@qrm5.@@|DC.@]%.@=.@ =#-@;-@ n#'Ф&[&)Rv&Faݽ% /%T$w50#/T"#/T"Фw5DA"M"~!{!p  @N٠)!p,=!   `6 T |D(@nV'(@X'(@YA(l(@`I[(@p1PK'@7+'@ю (@Xsc(@Py'(@(@y|D=-(@{'@p&@uE&@A$-%@%@#Rϕ%@oW$@i$@ r;?Z@?+?ߌ-@|' @REL @@@h@tڙ-@0h-@x7ͩ.@@=.@hҙ"/@$n8-/0@T+0@u'3@ @h@SC @ƚ\, @>w@|@+?~\9]Yu'3@xdk3@l&]3@13@7hi$4@4^4@0]'6@}8@ *GT@T@JT@M* 5CT@<T@z[xT@*p~3+U@#PU@eU@.|U@V@.p/ V@yV@LV@hj.>@3urغ=@db<@pZn8j<@5;@ g2B@diXB@TYB@m0JB@{B@&hC@>C@vj#!C@B@jB@H gB@e N@@ Y;N@q N@1 CN@Q/O@SNNO@\ P@f_MP@9 P@ 0P@D@&otAD@:9D@\HE@(P2`E@AE@+0kD@:ީ3D@~mlD@܊D@hYD@&0C@p0b:rC@3C@ zB@…oB@ H+KC9@pAY/9@8mA9@GO/*:@@ OK;@ ;@,+;@:Hx<@Ön<@1Dn=@"wߍ1~w/82(2~%ڨK3Zp:d4 5L4̭4Vb h|5jsB5E`6 9vXLM ,{MTMNAvXN "O!]E%OdWO0E<-4.ikO3Nb1^[3 ?W3~u<34q7 5,a?6 dWOr;\lNڬ(N`EMOgM!+{Lh?L0O OM LX'=L>L@.wKr;dKԬ PK,a?64%,av70Ve8XZ~8X~)9t:;9 m;P3Ye; vnj;GIIc;V-:̤?9 KJ0@p*j|0@3"1@_U1@ s2@#$E $@(< @]^X @Q$EH{ @AWx @  s2@2@(f2@=w3@iJ4@Pv4@zD5@\0g6@X7qA6@ o3k6@XA07L7@ki8@ &`8@: 9@U_G9@ >9@Ug:@G,qG ;@XdAH_;@AWx @>iƠ@Fiƈ@pH @,G@pKJ@P!@0"@`Q{>@oU+@lcSp@Teo@຦ǖ@@:~@N;@0~@@u(@ (96@MAH+;6@%`L5@X85@pG4@y3@q2@(82@8q޹c2@`1@`ӧ0@Ht0@>bJ0@H20@` q.@H&@Ȫf%@†U"%@U"@f"@UR&"@,@!@iC!@  @K@@7%h@B%i@ps@z @ `<@ZjR;@8:@0t{ؿ;@@"q<@8 J<@A0<@d۶=@(>S=@cjo<@b۬<@`<@<}[L">=aN>=lWڨ%>.~>vV9>9>ikg=A=<D¥<< ܹ(LC@\eD@aKW D@MH@.0bH@DYH@ أN6M@0P:fM@0ޛ0N@H>ȱCN@%M@xvN@آ.^N@%N@PG1}N@4 EI@RkI@"GfI@%{I@0 I@@tJk>@ffffff9 pAǘ;,P' @GA@ؓA@ƒmAB@%8cB@@ 'M&+q'e6g0' %8cB@. SB@0DB@S,6C@e6g0'Q3#'0р& & Ԅe_@K#_@ڪ(_@ |_@8]D_@doH^I@\DOgLI@&{bI@mlzeJ@KJ@ zU^@b2@^@XkU ^@ڧSs.^@8BJ@0-J@|W`J@BJ@ =i9kR@*[Pr{R@Rq({R@j#YR@7|R@M\R@-2C@@C@(RMC@Iq ~0C@XیB@ {<µB@ A7r져`@1dx`@O`@߉*`@avG@V!2G@rQ G@ 6=H@ `xZ@@z'Z@0CLY@o3I@=#I@HÝ I@ +qW@FW@뎃W@:9=I@e?I@,}fI@ 뎃W@zk`FW@+qW@,}fI@e?I@:9=I@ ~;G@~YG@ZTH@@wXH@cs2H@0lG@KJ3G@xO.G@ @wXH@SH@$2YG@~;G@xO.G@z3G@k G@cs2H@ -@_{f-@k0,@|k3t'@`TR'p(@Q)@ -@i-@k0,@|k3t'@ 'p(@Q)@ LA4@"-4@WV4@>4@L4@h㈵$5@u?@J0Zw>@@RQ@ػ?ޫV@CVw @oTA@ @ rD@k"[D@dXC@a<,@@xG -@`TR'-@ /6B@\"B@0mA@wWA@/rjA@H;lIV]jNVOk4rPIV*VVu t/@OPKG.@".@=p-@#R%[-@Q'',@ _oN@[N@l"N@ xN@m=nN@$}3DN@Sz҈Z{N@@SP'a?@ԞN?@Ni@@7}@@ ư@@˖ȕ@@b:73A@|5A@ lm_zA@ B@'@B@X&M^B@,_uB@}B@GB@xL FB@s.C@CC@*G[=@tZ2=@@LX=@5=@wHy>@`IJV>@ ->@,Z'?@KN@@@@ kHG@$pۺG@x>MG@`B5H@TuY=@P=@$ <@I]'P<@ FD5@mAP6@X`6@hU6@OAH)6@aK@ϲMK@J:mK@YAJK@ )K@ OAH)6@u>7@X;{7@ )K@;K@:J@ LqC@tC@cZC@aKW D@H^G@bH@6TdH@DYH@ ܹ(LC@,r\\C@$l\B@D&PB@6A@MH@$Ye4H@0b%1I@)I@uII@ 7ւA@<A@|Y,A@P:$2A@-wX@@l$ [@@485R4@@HﺑI@Ћ0bĠI@$ 1-@-+@0`n+@3u+@Q)@D-*@ =*@NN+@!,@ cjk=@=@9 W=@ر'SaѦ  |=@o=@ 3=@48EGʿWc?  3=@X)>@1w>@(ÖH>@8 ,?@Wc?,;?ATRaDW?- ?@ ŏ1w>@#=@ xK=@Ր @Biޱ@p\9g@  (`n)@)bC)@>(@(@0B[](@D -@qzR^(( 0v'@У (@p!7=)@ (`n)@&"*l2D  (`n)@_d8*@ RQ3+@0:7J,@)k,@Пc K*-@77W.@9&Ё/@؀\00@pB/@`,7h0@0@hD1@p(07~1@–ީ1@H"1@6q11@x1@HΠt"2@!Yd2@ s2@D0%(lJ$x? (OX ,|Q@OX_QC`g5G r^@YIڗ}:u  q0ۿٟ/K}?Na?4?@j@`p4@AWx @ KJ0@мt/@{VJ.@0,@0&*@#$E $@@f?~ոǚl?8@B#@ ʔ~&@@PaKX@@t3@@ 4?@ƚ?@lb?@wO>@>>@WGH>@s=@U_=@0c:=@3<-<@+J@\[J@d7J@`WJ@J@nlK@Dg7gK@)(pK@yeeK@!V! K@߇K@ʹMK@KA L@ J0Zw>@$Ö=@ xK=@ @e)ޱ@p\9g@ 1UUpnMUwLU$.V|A VK"VP5VnѨCV*VV*@@+@`A`+@Ft+@`+@`B+@@x~G,@U,@Q'',@ *VVeVB0bVvW*4nVʮ#VFVQ'',@䤹,@دwX},@D,@F큑+@x,~x+@ MY+@Ыne+@:7+@X0AG@%G@iF@ F@LbF@PbF@{\B@6;NѕB@ǎЕB@ :^m@@;%@@063a@@7)c# O"NCv" :^m@@p@@063a@@7)c# ϔ"NCv" ܹ(LC@MD@aKW D@MH@b4H@DYH@ #1+@GY *@: )@p(@X0z(@,;)@@^*@p,@p:0,@-5$-@:.@.+GpH@NH@. H@At& H@8\["I@t߅7=I@.]I@Og]H@0bH@Rq{H@{Y>GH@.+GpH@ #1+@x{V|*@0r)@q`< *@5H)@P!7=)@&`H(@`R&@P,;%@ )$@q#@ig>0#@.+GpH@rc@C5H@ت%H@+>VG@PjۻG@G@ G@- G@-G@ 귦G@kCG@"o5G@ MY+@OC-@A0F.@Hf0@30@X0AG@KE7G@^PTG@WG@mG@ 06q0@(m\0@ا0W0@X|"ň0@30@bH@^مG@г^@G@uG@mG@ G&A@0FoA@d A@'A@QZ@@L >V[@@wީo@@е@@ Д~"A@,$AA@P:KzA@BT`A@DaKz>A@07GA@!A@!!A@@@z77O@@,`xi@@tU @@p.?@TP#?@(8?@@'8?@ LA4@I.!V4@>d3@zG3@ 7{3@=73@&1hE@fsE@E@m^$]E@ZE@+j0 E@ OAH)6@xU"j4@8I{3@ )K@z(K@T6K@ `6@яc6@,&6@ԧ/m7@97@3&8@7@ThvH@;H@pH@yE}'I@b6I@8xJZI@ I@ 7@Em 7@f37@$K7@C077@PÖ 7@X;{7@ I@J@ FU>J@uXJ@~Q|J@,J@:J@ 06q0@XM }1@ m1@X=N2@w2@,,3@KO3@f3@!=2=4@@'\;y4@,!4@J5@h?m6@bH@OV! G@$!g2G@G@؄0bw H@bީAH@o "H@H@:)H@HO`x HH@AsOH@(H@K 6H@ h?m6@mAG6@`6@K 6H@AsiH@ThvH@ `6@hO5@ Q4@ؠttj4@y43@/07R3@D2@ԧg2@ThvH@b9z,H@5H@|9H@j5xʛH@_/>V)H@mɷH@ tH@ Q͖x?@g\?@U, @@<1ɗ9KJ:ʥۻ: D/f,E@ORo%D@r\-VD@(eC@x1 YC@(q.xC@DB@xHo^B@)ӺWB@LGr5B@ ƒm+B@BNvlB@{`B@B@t[B@4A[B@ZsB@tOB@hB@A=!B@62B@ (A@ XA@ƒm^9B@x)MNB@lBƁB@'A@)&RA@*LA@5A@ﺖ@@е@@ ƚ\<@|j@_b?o?C??r;?prX['@?U@)@l~")@(_*@Pb6;+@r,@tڙ-@ 1);<@h <@@B(<@Ö <@@$:;@x;;@`͖:@0Zی:@DF@(g2F@]/G@6gG@>{ڳG@:VG@V!H@K@H@ яc6@%`6@KAHc5@=5@^84@G@}G@AO.CG@u(G@8OPG@ ^84@D4@U4@%`{5@!5@t{$%6@t{u6@(Bm6@!Xy6@xU:6@8OPG@b}F@|KKF@c0F@<lbF@ौ<=F@cYF@3IF@a4F@uF@ xU:6@6@@"U7@(8@Dɻ9@>:@D >;@iAX;@[mގ<@uF@qE@Y@E@|-E@ZtE@4OgE@{еF@tE@fE@ [mގ<@}<@I>@$=@L&e=@L&q=@fE@ltF@thF@ 3F@o:F@ U, @@і39?@cH?@3>@a076>@XU\>@,`H ?@HIU?@Q͖x?@ʥۻ:wp-;bd/I; vpl:ue:S:u@9 ?9<1ɗ9 Q͖x?@ם?@t;?@3?@\01?@<1ɗ9m{9B^8;b17Fb@6 Tw?)e@@lX@@lk"~@@hS8@@T@@|f.\@@+@@@@063a@@L+Fl+HԩzW)t>JN( ܭ6'.f՗%`  % D{Z#NCv" 063a@@R@@3l?@Adb(?@NCv":2X! D+!?%l0! 8tcj 77@3&8@Jm8@9@'9@(21:,aK1nQ yZ1(-1Ͼl1 @"@@O`?@@3)@II&@:&@Am$@`C_@`:oG@ ꑹG@jyYYG@&ӤG@pQ\G@@T"G@XR6G@[hF@ ig>0#@pʓ !@`M{Vu @@"o5G@XG@7ȉG@`:oG@ t:@((q+:@B{29@~8@hN+7@/l)6@D@\[D@ D@.J7D@;WD@v0bCD@ -j5@LJZ5@Q4@ Ϡ4@e4@VcE@ h"lxE@;5YE@nD@Ӄ ~D@ )\!5@`3ɕ5@@ :-6@n36@[6@/l)6@p&zkD@ 4wD@`.D@PͳD@鷯D@v0bCD@ DkY@:iY@[PfsY@־HQY@r@ 5KY@}xv6@HyuQ6@L,5@u35@0A?o5@ r@ 5KY@-@W'U?.@0_.@D-0@Hd|0@ud1@\^5r2@L&W2@P-4L3@@&W3@|@E/4@Ӗ1j4@  eQ:LvQ(k9ؗQڨ{1(rǺ2hY2 eo2@Ht2@pfL1@ H0@4AH0@qUF@pdH6F@F@C&0G@!z@G@ 4AH0@/O]/@$xW/@@JHݥ.@ ħ.@vjE-@00-@Eq,@Sn+@!z@G@?TzG@xB&F@X* F@F@`F@HuYEF@nQF@R7 F@ MY+@pЫ(@raN(@@SdT&@0S&@fé$@X0AG@ؙ{`?bG@4ŎG@gHxG@ R,`G@:_rG@ fé$@0U $@~#@`Ы]"@7!@@jc @ H @E"@al@`C_@:_rG@Εީ=G@$N(G@hzX8G@$M.G@ȨG@E<G@gF@sF@[hF@ t:@pu:@ kK:@h"Ó:@D@X^D@iwD@Ќ{|iD@ c);@п";@t:@nV!E@x\H-E@D@ DW=@ xJ=@eK֡=@?K8=@iV4@!̶@tWW:vZ8O H >@ib07>@tJk>@IL@Ɇ.,P' Q= `iA@n!A@!!A@| U0=@ '8?@@'8?@ j@Db@@DȊ[@h._@5C^@%gܞ@JTfI@YI@KJI@`=J@ʝpJ@x}J@ P<@HJf=@DK?@=?@ON%DQ@Ej JQ@TɕcQ@DcQ@ 03@ _M3@;E2,_3@~}XE@\H-E@X SD@ aK_G@gG@ȯG@?)WG@؃m3G@(v0D@ aaD@*0 D@ VD@b*D@ ؃m@G@|O^G@}3H@-H@dZ`H@63QH@,NqH@` g2bC@C@ 'C@\O@C@FeC@̔ﺛ"C@(C@ d%A@ϠZA@rZ| A@u A@xR @@?"z@@@n@@l@@ %@@8́tP@@/$@*5{`@HKM@o_@ u@],#@ @oB! @M~^!@@(˶"@ 8́tP@@0rAg@@Z@@)i@@x|@@b)@@x|@@8Z(_@@v1^V@@Gu @@8(@@P ȋ53@@Հ?@H=(UZ?@ |>@@!#=@pˁs=@>=@d==@`„r<@cS8;@ۤc;@,;@fy:@U.2z:@ :Y9@` f9@\L9@K@8@ H 8@еl18@07@@(˶"@@>"@샽#@HU$@yp%@tD&@`9[(@ :l~(@Ķ (@`HX'@R\'@'G)&@ `%@Fמ#@ z2j#@~m$@ir|z+$@L #@@]5#@ "@ "@@]5#@`GF#@O "@ix#@`PE$@ro{$@`aD*$@Fמ#@V{!@^;u!@`Ђ6M=!@ XdAH_;@O;@m<@ xY<@xM\(=@ xK=@u(@0g(7@M &@@r@0@p\9g@ 8 ,?@:>@w>@J0Zw>@@m@y @ @ F@h5F@9~. F@|uWteF@{\B@y$C@fn+ͶC@oCC@ /jE@k_MOE@P:D@YˋD@.OgD@rv0bD@ D/f,E@`FccE@L8E@\:ӏ%F@PbF@BNvlB@@\QPB@D̠B@P1B@ǎЕB@ (\2@0G۴2@63@=73@33333SE@!X4E@D$]E@+j0 E@ =73@NbX9t3@r߉3@_3@g67^3@+j0 E@m4E@EF@b('6F@X9vnF@  9ԧq>@)23>@`m=@cjk=@ s `=Q~ر cjk=@Q=@333333>@= ףp>@-Gq>@رGzRQGz mA iV4@Ih˹>@ٙB5>@8̒>@k>@%>@P\0=@Z8O|yvg?RDF Gɫs jH\h+? P\0=@>@1RYl>@hCK>@?6>@(>@x]>x>@h+? M\/1m R( Ќ7F ܺvNO dWO1YlOh$TPO!PBv=PNd~P7LP,a?6#f6dy_5uO61ڨk6:L5貋e6 laW@tf1hW@)W@N +W@44@7hy5@4dR5@u> 6@ B%+CKdܾ}K9rK~ѕKSCc L*K?sK› ELJ㝪~@UԢ0@A>_@@ 9@)0@AX. -@#*?Aqd? Q1yQ&~ݲQڶ`+rQG#QKW9R(q|=RۿNRGR ?L]nR*5LnR\y.Rq1uYמQaBQ̓> Q"?aQW1ۂKR]: =`Pg&f JT[ J`E.]PfGCQ $P"1("TV #ב/q$x<ګ($W"Hy&!9=y?&hI% c'Q`('Q`(\?`׻?@`HfK@~fK@GzK@ Y9K@ ׻?@`9(av`Ù_` Y9K@ػ?ޫFL@ܘL@ %R`@ɔH_`@f6`@708`@xVef]H@S"G@*G@8R{H@ 708`@K-6`@nϗ_`@%R`@8R{H@`}G@#G@xVef]H@ JC#v]@5]@jP2 ^@z6>/^@ڧSs.^@#II@`JI@_I@gI@BJ@ ڧSs.^@>/^@>mX ^@ s ]@JC#v]@BJ@\V!hI@0b`I@߅JI@#II@ Aa}^ ,2A,R?͙p rH#@c#@0-)#@]X8#@@0ۤ#@V0N$@B6ѽ$@ BRR.CRєRՇR P]N5jRdO]NDRRQp+;,yQqQ QSrTQ8QQ1yQ˧uÿG J9zIc G*1x {MGypr[/S2`hf r!W1 @P=|SP'P!5P/k0gPgPW(PsP;PdPݨ`Pg%P7O|P瓞QZ%u?QH:L=XQQ%&QQsQoN]α RzzrR7R%R A&R͋E*Rfz2R|SSRDAR49RڂV'RRUTQ>C8Q2{ ??+(à|+gH5+p(>,p9ޛ,8Ae_-'B.Y, ,)@Pц~(@ )yI(@9&y^'@p ['@+Z'@-PK'@h>'@hBװ&@ uF]N``YN Y^ MWS5N^%+Z:M/~M.yu_Mr;M8NMi@MxBEM^C%EMz6MMHF겚LP>L/dL› ELw@@.GZ@`f@CL@@e^@ @'j @aP>>@??#J?gJ?A%hNl?>x?B1?5-??Iu?Aqd? › ELw`g6Lv L^T6LlM9vXL\2@0_N@%!@@ UK@pg@j@ N +W@qzաJW@NCW@R9.SRW@wTW@NԆW@ȩJ^W@@GW@W@i1W@!X@pHX@5SCX@26YX@oTX@u> 6@ IG6@~x6@`1- 7@4F8@H5]'7@1}kܬ8@h-L)9@{U:@ԞՒ:@ࣉ C;@0.r;@0 ;@;@7LB<@  4V@.V@LV@xL;@ȱ`<<@e;@ p"\sD@ڊD@y]bD@xnC@DC@.=rC@˄_UC@ 7C@XL$zC@X#smB@J\B@ A@ A@d%A@3~Y@'KZ@9@0@׵@G`` @od @_ @D @k @@@@@ @-؂YZ@/$@ d%A@O_bOA@q= ףA@/$@$tc@@ 0 `@dx`@۾_@ U_@@ l=@<@N$<@ o1;@ $;@ *:@x{%:@pP o]9@18@{a8@8@4=8@TBx7@ KY4'@ n|)+@qI,@k+ݳ-@TBx7@j^ 7@}6@pJ6@ 4H~:@x`:;@3<-<@rlһK@9CK@KA L@ 3<-<@ ";@";@(t{I;@KA L@k71aL@tCL@(WL@ (t{I;@:v:@OQ9@`"*9@"P8@(WL@8ML@\q{L@.L@ROL@ tWx Wn4W kWNVVU`빝VgeV#EtVQL}V1:Vr V)-%VVYV:(IVPX-@) =-@7 =!.@;.@d 0@0@\)h0@Ƞx0@0@8I!0@̠寎@1@A?1A1@M1@MZ1@(Xg1@  @ i?~?| )O??л.?BUڏ?3j?:Ra{?mI?e@` Z @G9@p9@TDڌ@ %!@N]ۃ!@Raw"@`t #@P4ǎ<$@`~$@@q&@`8H#}'@߹'@q (@`&p(@,;)@` 3)@\U*@1 *@gY%+@?,@@m,@l0؃'-@_t\,@f@p-@b̏(@]@0w:'@!_X8@os@@ D@3@d @(љ~o!@xɗ,"@p˗,H#@ϜdR$@Mݘ%@0 %'@h;;'@Ȅj+(@(@ (> lTy[Tq:T-8rT( B&U`UH)U c{{4U_+;U`CUU#!IU 0UJUx&$`UPlU߫iHsU /)U8?"U1dHSUVSUx Ul [Ub^U8DU #R-@,T.@ -@vI-@5^-@ 8?:D>-@@MgU-@Dޠ-@{-@@-hc-@-@Q'o,@3?:t(,@Ğf+@Ğf+@DPK,@ 0+@S蕎+@ކ{+@iI*@@ '*@`u4 *@()@  f2L@ZpK@8r\K@tjK@ }3K@BNK@$qoK@몚K@A?8@cÍ8@(qE8@8Bk!8@{7@q]7@;j7@U6@ rJkxS7pSE[So.5OSa^SxvVS+K$@0=@l_@L6@Ҩ=o !@P4cLW!@ R#Tq|TmVTʚTVTxT a{qTcϽTnj!#@R "@Hљ&"@HJ~!@R!@ͩ@!@Ѩ= @ 6s @ P{u$8_%["$U>O%w >%0T%&<& Pa' L(Ps\M(R:+1)8³l);~*, @* @VX[n!@P!@+"@a`#@y|$@PG$@U #@hp#@#R=#@Sw"@`4c\!@ ^yH@pxH@+{ xH@xH@hxH@(r\M>H@wg G@^!m<&@=&@@;%@ љn#@`AK"@ WXݬ!@K7 @ 4oY@BGY@ܦNBY@Z@pNZ@__(@HV*@Ls,@7X_,@j,@ b }Y@+:Y@J?DY@wIY@p1tY@A 5 Y@a(@`@@p@@/ 7@V̏D>@@^̏@ J@r\pfK@4Lf{K@hL@p~FcGOL@CĊKL@:e7M@h^ M@pV0N@CN@03 N@H)(vB@:B@~#0hB@:B@fىC@>KʽC@Hq B@KܴB@KJCB@)>B@|5A@ 03 N@`$O@,j ~O@'ŘO@҈O@͎"P@ `//P@ReP@4ioP@ GP@ 0P@|5A@f$A@<7A@zUA@(HB@R'B@F/& PB@WB@*xB@…gB@…oB@ 1ȱK@`EK@?)`K@pr\* K@yxJ@xP@J@D@DCD@(\۠E@j|)E@5E@CCD@ rc "@ #@=j$@#@~F%@x%@SP&@9YB&@ gN>@Ú1>@P[:>@F`?@ Hy?@Ox @@78/@@ @@ @Pu\ VٔP:/cPWnVPN'Pm PEQPr4OՄ Oz;GPq<*PǫiPd:4P;(PL 8OnOpZfO[ Oܬ({NZpLNuF]N2{ ? i.?ڒA?WF?BUzB?Aػ?}Pjr?:q@AI@@ E{ @Rׯ`@p;i9@lc @Kz@:!*@Kz!@@,X@sW%@!!@w@ uF]N<8΄Nb(+ZؑN겅"KB%+CK<Ͷ@_4Y @sA @ )@J㝪~@ B%+CK\-١E KȐJiJNCJkFxJ8GJJJ1!2IJ㝪~@z.@o|@`P>ޭ@aOWm@_pc@ @` @lc@ :(IVa]ΦIV"'NV|n(;V(Xg1@q1@L/@ H/@ c'Q`\?`׻?@`HfK@jzK@ Y9K@ Ù_` ```ܘL@ :94M@!<8nM@ `x]``Ù_`!<8nM@4M@ܘL@ LqC@C@,YFc[C@aKW D@H^G@g>`ki"6L.. ?dmֽFIۈ@v*oS5/@\ ѿr;?B6ѽ$@B %@Ɇ&@i~m'@q\)@*t*@0;*@*@y +@.)+@88p*~,@@U6-@-@@ y;.@ ?-@tڙ-@ LV@fʦU@@FtU@:9@U@& ZU@T@2@0.7l>@hj.>@ =i9kR@p}R@JtR@q(R@&R@:ڎ!S@9S@fS@I/WˢS@JT@9T@-2C@)({C@@:mC@~.D@ GD@6D@ I7D@l"nD@4mD@ @Q@T"Q@ 5P@@mSMP@pC Q@*Q@¶P@d^GD@D@Rп$?D@^ -EzD@"Y)]D@Θ: D@>.HC@-MC@:9C@,\fsC@V'OgC@D)B@  0P@ni(P@¶P@…oB@B@D)B@ 1Dn=@K=@t{R>@?M>@\01?@E`6 e *6}XZE6Q &6Fb@6 \01?@B[@@A@@+wrT@@Le.b@@ UN@@@5F>@Fb@6 b15-w1e4M4 13BC\k3)2&]1sB0[^d0PQ0m0{̜͸/d͇X/ / 5F>@XmAO=@ U<@`x"l<@w<@t{&;@GY_ ;@hw:@3Ha:@H+KC9@ /0e6J/\sik 0:gc0Ow0 wsJ1Œz|"1}1H681"wߍ1 Ԭ PKH'6K-١%K>%K}SK IKqKKzkBK9+*K&~#K#arL9vXLM̤?9S)98-a8l(ɗ76yS8z7dnn7~D֧6@U_[6}(6+;;H6(6ik40E<-4 XdAH_;@6;@PEw:@Ö6:@ؚK9@ 9@XI07l9@w?8@07@u(@%-4@Kp@@giƸ/@?@[R@2L@(~Xu @`Ђ6M=!@ 07@e7@]짎7@ 7@8!K@6@(96@`Ђ6M=!@]'U!@$%!@d"@s\#@y޲-$@u m%@H&@ ` q.@&O]-@.&-@dj7,@<,-@-;,@0(O]-@(O].@@w.@Q{/@@>7/@KJ0@z @`NM@Ng@[R@PT@ ="0@Kp@_ @ Z @Ѓq@u@#$E $@ 6q0@1@Y1@=1@D1@>~2@#ɥ+2@ef2@p–2@ԧg2@iLH@PohH@gfH@ѮsH@ѮH@d@H@F3H@hQH@nީ\H@ tH@ ԧg2@X(d2@fB1@`1@ӧg0@(Y/0@ -0@ )j=0@@`.@:.@ tH@0bH@FqFI@xZ.I@+@I=@ CH=@H&`F=@=KFj=@ ҧ =@I&i=@ <@0.ԧqB<@ Ņ;@~R:@0Zی:@DF@rAsgF@}QF@g\[!!G@1G 8G@{/xCCBG@}0G@>Qe,G@C6G@O{=CG@ВAsQVG@vG@^G@P?OG@t&G@}H@HfvH@;H@w!/H@K@H@ |uWteF@tI^yF@ F@ u=bG@oCC@>VC@ KoC@4%^C@  u=bG@e F@ :MF@'9M)F@ AF@|uWteF@4%^C@XC@C@DWbC@ZoC@oCC@La4@X@ܫcꉕN@xB&e`@DcEF@nT}f C0$@)x)?@cێ4@V25c%z#5@={i@civ5@XWc&Uϸ6@ d@f}VF0!"G KK$EA @ m㵷K6`YM@#&Z@s^B@ӯ`@ۿ F@VW _F@xIB@U|*J@f@@[!c(G@0R"?=@lǒr?D&ZZj"!@zѦ)Κ8@׿k`A@AP)^_$6nn5A@o%%$24G4@ƪ uD@܋h<@)LR#=@{%N@qB@VJ@NFGn@Z_SI@S@>@oGVQ@- -@ P@s89@҂LP@9Z_-r) n rUJ@"KP"<DK@^>!$CU7)@C% ULdK#@w_EE5@K)VK@JK~O*;@@z !M@ _khE@ #@ n=F@Ibn#@3kocA@2%8lC@&g6%&>3Y@#}G@,iLy*@j[@/M (@I+G12Slg^-4@>+:E@~_ 4@D@0؅N4@goFE@3c!PK@Y 8@4n[;Hx@8h"@67m~~$/3@Yr nInF@5?g4@`PF@895/vj?@(5J@:;_!?@Xm"z5J@<e'ƻ\@b@=>aT`@SE@AB=@)?@}@@Qbhd?DGE@6p'@E C@("d!@FGf=D@pu9+@LWdC@s-@MEu٤.r*@N04 V3#ټ/@O#)UDnt-@PQ\UI2@/ɋF@RSs ,ծ23@i"1F@TE@:K]{@@X5RzB@ˋwY?@YZ}4@[ $zE@rs4@A{D@\ݨA@r@@]7@yK@`aH>:e.@`c-5@?&M2@.Sl.@bS.{O;?@7H@cd߸AC@G@efr!A@Y~I@gh@} .>@Xi6EF@ͣ08@jNxk-a1a,@kQ !@_D0@mnbBadmQ@op6)؟av:yO@q.v 63@^n(G@wxXAnRH@+hG@)t+fP@AG@y2w;@A^J@~g.@<G@4?F@Ʈ!B@'r'@@J@݈>@Q@ Lk@@)A@P@@|w wA@kR~$@:I@{2#@$@#L@$͓F@8 D@#%"A@t]9lC@7E@E@^S3@WSǥG@~_@\L#\ _@c^~!f_A@b?)“?@"@W3@Ք\E@*H㖗I@c_R9@n]3@P^H@h*@A G@>R~A@*3 18@/`Uy<\B@ܭ&WoTC@$A@55+~T(@y8U9@F@o w;@Ρ3*7@wo*6AP@u-2A@#g @Q\cG@K r\@aH@= i5@-]ƽC@B5@ )D@,ct59@􁬑lhE@xgU%L@N:@/9@hd;SM@bI*/@0tEF@Z0)=@< 㣣jlEW+͢L@2a@>@{d :@g:EA@!}>@\p<@aPAM@rr @)A;I@X`@g<)E@QNV`@\'E@r3@cU8E@$H@v}uD@K@G@: Û#D@Y>@ne@{|Ad>@VqB@eG2@̋QHE@|1@>JF@5(D@tlZ$Ή3Rx>@ףp= MX`@ӣ')E@ٸ~>@@T$* } _@kmgh"D@Qy2@+G'o R2@ đJ@]*aG@-s'<(%!@LD="XwM@1+J^!v@-Vp:1@RsY[:8@ tWFݞA@ɥ7?@/h=@,c(B.@5E(9.Ԋ(@Pm:d@\"%Rdc@7!($%v`@|FnG@ sDB@0L F/@}R 8V[y+@QuD[3@pWJ@6qGX@K3@v .XQB*"R( ()@%(}E@#TF;@eQD@Ґ3;1@w)Z.(!8I PQU1'KEC+Id6P6(DB:fO6T.doW#S\9s0JH:vـUS2,^x!"mR:DŽd_j_6@/gRIQF:XCX Rn&e:QWQh!6wKT,bQ@vHp!Tb@pFDA%V`@c8E?̦V@b|7@%zSH^y8@0F͌qS~E:@$%zMS@D:@Z+ȦKt wn%KӠxO7o5G@ $:O˸KH@Mq _gbǯ.H@RiK|Y%_H@ 5q`DJ@S('O@KlTL$JO@|eF/TC- P@kE/ SQiP@QlXt[rB0XQ@c&&&O@4[w8Q@ulZ0TR@1͘Sa"NR@Q"QUQ@vӿXF?*R@q]WySR@u\h2'I^$WBR@ѯWv?*FR@{h"YbR@yK[8rR@5EW%VR@,w]ӭ.S@WjVjS@<"R[CYx2tS@e-}Ƭ[b oS@߱jIXL0nS@;SY@WS@4V7S@ d%Sek.T@ *:ۦQhq K AHso[@<^<3@  Z gI<_@<]7 I@x o9sj$@[ K R呠JJ@ԑiSyV5@$~L'@tOK@bS^b>P&ū)LD@$ F@/!@/O@f@[Hh1撂h5kf@|o("0Ր MnDaId2"@E@! |'@# φ^6{`J@$%NE@Mg'}E@&e $@'w‹Z&kPz$@(dF8@PݞA@)CL-DJTRS@*>]yM< @,]@j#-Cz]@ʾ G!.|LL[^@#$!/M^N[@ab0ߟRi`@Z+G{y1=e_@|IF 2e,`@{ 3Gu= a@z[Ĩq4.,&^@@YA^5_`@xc?6Ϯ ׈\@ܻ43?7?7.HY@)gHR?8ZWpT@c7@:NP3&e"GP@;t><,@V;TbB@<{!@5 3F D@=ɜQSU|!2@>;-`@|K@@?8о(`@ EA@@U%W ha@/իE@AB_L@FxiI@CD a_O@ %NK@EFP@h&dK@GH ُR@,dK@IiB@}?J6I 6BZ@0 ?^)@?,"Y@ :sB2@KƐ_@!=B@MK}2@p;@Nm \(T@g>f(@O)8@?2!eL@P(Ps"<@Q_}G@L2ST~ [X@!W{H@UVjFY@EcOqI@WLΉY@!2,@X4o/#\@~P @Yڃo^2@(vk5Z5E~d@>U@5[\C\8+@I/ .@]H @k&C6#@_i6"86@g׉zS@`]3/@T5US@aGE_6@,ԛT@b?We@REcw$ ie@TNUCdJdK@D(ݗ4@eNMTQ@z5>@fi Ts @g(_@l[3m@hv^@g{$@i;]@Jȋ#@j`=y^@wp_@κ&@l:_o?^@#H*)@m!?0^@.@n֔,ec@c{&o%~b@ l4pWp.'b@Щ%qc@?& rNQPƱ֭;2@sGSq 2IC@t;]aa@HVH@uzM@epTP@v?]nf@`sQ@we5L]f]1Q@x^PEja@эP]R@y>c_b@[R@z70-a@VR@{t5L@XCR@|']Y@DZeעS@}uo H@>,VFM#T@~ 6W@SS@ZU*d@;0i:;#`#d@2 "Y m;c@jO?tP G@@ A00@Qz3fO@|[jY@\ݩ*@HfBAAM@6"2C@ͰGNxF$@۽_Z#@ܙA@R%?^@:(7@zo9L_@E>qc3N3@RqatWC@F? .c4aL@Edm] N@!#Ie;p'O@[P @6Z@{,W0@_U)d@Sq.HpG@H/@__@zJ@3̥ PB0t;Q0@ s?b U@xta.<@gd R@06֨D@CQ@n3[C@hE*rP@D@}=@UR2̫/ Mbcl7w 4@u>~@Jp+<@ģ= AcC@H@b Z@`I@'(oz*W@ SI@*+R%oG@L.H@-.dc-@ )@t8>@0Q@HI;D@;Tb,@d C@J AH@W96@@.ҥ"0h/@wH@Ӏd?@ęai|:1sALe<@76aG@[q?`fK@G `@_q OG@]@|~I@Bb$``jWVM@{YV@9f;@Fa6ŋF@˔C@gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/vector/country_boundaries/dbln000066400000000000000000000001451510331751700265230ustar00rootroot000000000000001/country_boundaries|country_boundaries|cat|$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db|sqlite gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/vector/country_boundaries/head000066400000000000000000000004441510331751700265070ustar00rootroot00000000000000ORGANIZATION: DIGIT DATE: DIGIT NAME: neteler MAP NAME: Admin0 boundaries from NaturalEarth MAP DATE: Sun May 26 14:29:10 2013 MAP SCALE: 1 OTHER INFO: From http://www.naturalearthdata.com/downloads/110m-cultural-vectors/ PROJ: 3 ZONE: 0 MAP THRESH: 0.000000 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/vector/country_boundaries/hist000066400000000000000000000014751510331751700265620ustar00rootroot00000000000000COMMAND: v.in.ogr dsn="ne_110m_admin_0_countries.shp" output="country_boundaries" min_area=0.0001 snap=1e-7 GISDBASE: /home/neteler/grassdata LOCATION: ecad5_grassdata_ll MAPSET: PERMANENT USER: neteler DATE: Sun May 26 14:29:10 2013 ----------------------------------------------------- 286 input polygons Total area: 1.47751E+14 (360 areas) Overlapping area: 7.66232E+07 (70 areas) Area without category: 3.87927E+11 (1 areas) --------------------------------------------------------------------------------- COMMAND: v.db.connect -o map="country_boundaries" driver="sqlite" database="/home/neteler/grass70/demolocation/PERMANENT/sqlite/sqlite.db" table="country_boundaries" key="cat" layer="1" separator="|" GISDBASE: /home/neteler/grass70 LOCATION: demolocation MAPSET: PERMANENT USER: neteler DATE: Sun Jun 16 19:28:19 2013 gdal-grass-2.0.0/autotest/ogr/data/PERMANENT/vector/country_boundaries/sidx000066400000000000000000005500051510331751700265600ustar00rootroot00000000000000q _9c*Ahe)Vd;Oa!rhmQ@d;Oa!rhmQ@w je7aJ1amQ@ je7aJ1amQ@x5aJ1amQ@5aJ1amQ@v1%Ja`q8M@1%Ja`q8M@ `!<8nM@`!<8nM@Lqtb`ģvK@qtb`ģvK@Fʒ3 c8!ӎL@ʒ3 c8!ӎL@Ù_`ܘL@Ù_`ܘL@ ҊT0%@ҊT0%@&(> lT #R-@(> lT #R-@R#Tnj!#@R#Tnj!#@JxECmUyH-&@xECmUyH-&@%cϽT 6s @cϽT 6s @8DU()@8DU()@V1UU*@1UU*@UxvVSP4cLW!@xvVSP4cLW!@g3b9S,^5f2@3b9S,^5f2@ ["<1X4@["<1X4@@@::1l5@@@::1l5@ v0 #"0@ v0 #"0@hˤf+Y, ,)@hˤf+Y, ,)@ lW(;-@ lW(;-@ n#'|D(@n#'|D(@T i$@T i$@p B6ѽ$@p B6ѽ$@AarH#@AarH#@ G"QqD@G"QqD@pdI,3ؐK@dI,3ؐK@$`XnhMB@`XnhMB@m4tVX^ J@4tVX^ J@#L%[xA@L%[xA@ Rk|{`E@Rk|{`E@oi? hGQM@i? hGQM@zxs-HV-P@xs-HV-P@_'T! ;@_'T! ;@ʒ3 cģvK@qtb`!rhmQ@q1UU 6s @3b9S,^5f2@M["<1rH#@Aal5@)xs- ;@RkHV-P@|oc1S|oc1S0cc_y~^Rcc_y~^R3AgM^5t:`RAgM^5t:`R2UFѾX6쒿QUFѾX6쒿Q4|']T~b%= |']T~b%= 7SA8ކC?7SA8ކC?iKToP\rKKToP\rK+NINIvbNNHI_L,~_7>?Ԭ PK̤?9Ԭ PK̤?9>B%ԯJ3Z@B%ԯJ3Z@@$PϓF60S$PϓF60S1|oc1S7SA8ކC? KToPHC8Q(͆u'@>C8Q(͆u'@f QWT@ QWT@_- PSPBF@- PSPBF@#gPbkǃ2@gPbkǃ2@~:xOh:;5G@~:xOh:;5G@B`/d7gWZmFR@`/d7gWZmFR@TP2*LW:Sh1R@P2*LW:Sh1R@ReU|JR@eU|JR@PxxU%%HoP@xxU%%HoP@I_vOtUoŏS@_vOtUoŏS@^^#W줃S@^#W줃S@\#ݛsWfFQ@#ݛsWfFQ@KFOȫWz!qCFS@FOȫWz!qCFS@W=?W:ڏ(r_S@=?W:ڏ(r_S@Y[z\{S@[z\{S@[_Vv[S@_Vv[S@ZϽ [w;S S@Ͻ [w;S S@Vq= ףPZ)\hR@q= ףPZ)\hR@N(\bYףp= WR@(\bYףp= WR@QwgX+0du%S@wgX+0du%S@U.YmȔS@.YmȔS@]c'Q`HfK@c'Q`HfK@!3_mw/H@3_mw/H@D(\µ^H@(\µ^H@"= ףp^Q@= ףp^Q@S \i>"TR@ \i>"TR@M2H ]`LiS@2H ]`LiS@X׻?@` Y9K@׻?@` Y9K@sNb4H@sNb4H@C__zM@] @__zM@] @~ (LpWI@ (LpWI@EaG#0T@aG#0T@}= ףpNHz%@= ףpNHz%@: xT)3@FzVTQhdFR@y oG8-H]x,~x+@fVhD@@UvTQ(͆u'@~:xOWT@1`/d7gW%%HoP@_vOtUoŏS@ ^#WfFQ@=?W줃S@![ףp= WR@wgXz\{S@#c'Q`mw/H@ \`LiS@%sN@] @aG#0T@}' [mގ<@fE@[mގ<@fE@1);<@DF@1);<@DF@xU:6@uF@xU:6@uF@L&q=@o:F@L&q=@o:F@0Zی:@K@H@0Zی:@K@H@яc6@G@яc6@G@h?m6@K 6H@h?m6@K 6H@`6@ThvH@`6@ThvH@D17@؜c=A@D17@؜c=A@|%`3@evY>G@%`3@evY>G@03@~}XE@03@~}XE@^84@8OPG@^84@8OPG@LA4@&1hE@LA4@&1hE@9YB&@ @@9YB&@ @@#1+@.+GpH@#1+@.+GpH@MY+@X0AG@MY+@X0AG@*O] R.@{ָB@*O] R.@{ָB@nDp2@kVF@nDp2@kVF@X?$Gpg3@BT)nF@?$Gpg3@BT)nF@Yԧg2@ tH@ԧg2@ tH@"P8@ROL@"P8@ROL@4H~:@rlһK@4H~:@rlһK@(t{I;@(WL@(t{I;@(WL@?@^J@hlj>?@^J@C&6?@ZF J@&6?@ZF J@,7?@ J@,7?@ J@Bʔ~&@@+J@ʔ~&@@+J@A485R4@@H$J@485R4@@H$J@B&@HMmM@B&@HMmM@&PΧO.(@{ffK@PΧO.(@{ffK@k8I{3@T6K@8I{3@T6K@(#H5@%jOL@#H5@%jOL@fFD5@aK@FD5@aK@'X;{7@:J@X;{7@:J@OAH)6@ )K@OAH)6@ )K@7@ I@7@ I@7ւA@HﺑI@7ւA@HﺑI@mД~"A@@@Д~"A@@@e6A@uII@6A@uII@lXA@)&RA@XA@)&RA@d C7@ qP@C7@ qP@sP<@ON%DQ@P<@ON%DQ@"p+YE4@NFQ@p+YE4@NFQ@r=?@DcQ@=?@DcQ@!(a_8@"lxzvS@(a_8@"lxzvS@?K 6>@AN@?K 6>@AN@2[<@^ĕPs@N@2[<@^ĕPs@N@&Nw@2@xS@&Nw@2@xS@@Yr9@wT@@Yr9@wT@g3@ #nF@g3@ #nF@?@[)3@ {F@@[)3@ {F@Wg67^3@X9vnF@g67^3@X9vnF@>@007c3@ڿ$FF@@007c3@ڿ$FF@Z=73@+j0 E@=73@+j0 E@ h?m6@؜c=A@L&q=@ThvH@5+%`3@~}XE@LA4@evY>G@-9YB&@ @@?$Gpg3@ tH@."P8@rlһK@ !<@(ټM@00!N?@ZF J@485R4@@+J@2B&@ I@7@HMmM@47ւA@@@XA@HﺑI@]6&Nw@2@^ĕPs@N@=?@wT@98g3@+j0 E@@007c3@ {F@: s2@AWx @ s2@AWx @KJ0@#$E $@KJ0@#$E $@/7@%/7@%9 W=@aѦ9 W=@aѦyw(@Эg~fw(@Эg~f|0B[](@(0B[](@(8@@0&@0m^ @@0&@0m^ }0&*@B#@0&*@B#@y (`n)@D (`n)@DKY4'@TBx7@KY4'@TBx7@_t\,@(@_t\,@(@tk0,@Q)@k0,@Q)@uk+ݳ-@pJ6@k+ݳ-@pJ6@sB1.@0LN5@B1.@0LN5@h)~.@Ф0@)~.@Ф0@3u+@!,@3u+@!,@-@|k3t'@-@|k3t'@xk.@P ur4@xk.@P ur4@g(96@H&@(96@H&@+p7@@O3@+p7@@O3@9@6@9@6@%` q.@z @` q.@z @07@`Ђ6M=!@07@`Ђ6M=!@ 8sN4s2@bp=E@8sN4s2@bp=E@;E2,_3@X SD@;E2,_3@X SD@+e4@S:XKE@+e4@S:XKE@907qg&4@mC@07qg&4@mC@vq 4@c*E@vq 4@c*E@:)\!5@p&zkD@)\!5@p&zkD@e4@Ӄ ~D@e4@Ӄ ~D@c:3P4@U+~D@:3P4@U+~D@b-j5@VcE@-j5@VcE@\"ja6@A&xC(E@\"ja6@A&xC(E@/l)6@v0bCD@/l)6@v0bCD@c);@nV!E@c);@nV!E@h"Ó:@Ќ{|iD@h"Ó:@Ќ{|iD@@@0&@%9 W=@AWx @=KY4'@|k3t'@)~.@TBx7@?` q.@z @9@6@A8sN4s2@mC@)\!5@S:XKE@aC-j5@Ќ{|iD@c);@A&xC(E@=E:7+@iF@:7+@iF@30@mG@30@mG@4AH0@!z@G@4AH0@!z@G@6q0@iLH@6q0@iLH@06q0@bH@06q0@bH@`DG=,@xPTJ@`DG=,@xPTJ@ :.@N3I@:.@N3I@fPX0@b<fPX0@b<(\2@33333SE@(\2@33333SE@eo2@qUF@eo2@qUF@ Q3@jJ8 Q3@jJ8'9@Ͼl1'9@Ͼl1H+KC9@"wߍ1H+KC9@"wߍ11Dn=@E`61Dn=@E`68tcj 77@(218tcj 77@(21)>C@G@>C@G@i4xh C@ДAsG@4xh C@ДAsG@jjkeC@x@@jkeC@x@@[CC@@@CC@@@\%8cB@e6g0'%8cB@e6g0'LqC@H^G@LqC@H^G@k=C@E@=C@E@ D@6E@ D@6E@h(D@@cZ$h(D@@cZ$:7+@b<06q0@xPTJ@H(\2@jJ81Dn=@qUF@J>C@@@CC@ДAsG@L%8cB@e6g0'h(D@H^G@NSr3Vf"[='Q@Sr3Vf"[='Q@bX9e$@M-P@bX9e$@M-P@.PyelO@.PyelO@A픵d @%N@A픵d @%N@B&9 cZd5@B&9 cZd5@ r;?tڙ-@r;?tڙ-@Xqc^3@Xqc^3@嚈@lc @嚈@lc @=@h@u'3@@h@u'3@@l:@CH@@l:@CH@l,@(3cI@l,@(3cI@j@JTfI@j@JTfI@ m !@`.;{K@m !@`.;{K@wi^!8|Oe;@wi^!8|Oe;@Y}8@Y}8@O+B?`"%!@O+B?`"%!@ao?`0ɑ@ao?`0ɑ@w&@༽ޡ@w&@༽ޡ@60v'@&0v'@&7 %!@f@%!@f@@ @^$yB@@ @^$yB@ @3u#@h\ۄE@@3u#@h\ۄE@w\@E@\@E@Sn+@R7 F@Sn+@R7 F@fé$@:_rG@fé$@:_rG@ig>0#@"o5G@ig>0#@"o5G@dAw'@ HM1dAw'@ HM1*`7#@KJ@D@`7#@KJ@D@ t#@\[}K@ t#@\[}K@uTtZ@huTtZ@hz&'[@7Vz&'[@7V @ @&s\@ob~@&s\@ob~@EBsx]@TDڌ@Bsx]@TDڌ@d= [}f`L t0d= [}f`L t0 {cyR5@ {cyR5@ PCcbE-5@PCcbE-5@ 1|DLc7ۤ4@1|DLc7ۤ4@["@s)?["@s)?3u^L#@٥[E@3u^L#@٥[E@: d@02aw0: d@02aw0 l30@0~5I@l30@0~5I@Dʾ@p]H@Dʾ@p]H@`C_@[hF@`C_@[hF@x%gܞ@x}J@%gܞ@x}J@ rc "@ gN>@rc "@ gN>@@`:oG@@`:oG@]@JN ]@JN pWsa@#~j^R@pWsa@#~j^R@U"b@^I R@U"b@^I R@.4ib@1=aR@.4ib@1=aR@uDZ@ ^S@uDZ@ ^S@$nI@F%q#T@$nI@F%q#T@ %X@?o*RaS@ %X@?o*RaS@֌-@pi@I@֌-@pi@I@A[gnS@{>@rc "@x}J@i]$nI@JN .4ib@F%q#T@E_֌-@AQrmR'@c @P|hBI@!aʒ3 c 6s @Rk!rhmQ@|ocH@ILH >@ILox]>x>@NOx]>x>@NOnHPYa=@!̶HPYa=@!̶zP\0=@h+?P\0=@h+?iV4@Z8OiV4@Z8OtJk>@,P'tJk>@,P' -Gq>@mA -Gq>@mA 9ԧq>@ s 9ԧq>@ s ŏ1w>@Ր @ŏ1w>@Ր @Adb(?@?%l0!Adb(?@?%l0!8 ,?@@8 ,?@@J0Zw>@ @J0Zw>@ @XdAH_;@u(@XdAH_;@u(@cjk=@رcjk=@ر xK=@p\9g@ xK=@p\9g@Kά@@ffffffKά@@ffffffnLOXC@ۧ1nLOXC@ۧ1q= ףA@@q= ףA@@d%A@/$@d%A@/$@ 063a@@NCv"063a@@NCv":^m@@7)c#:^m@@7)c#2(D@Z$2(D@Z$/8́tP@@@(˶"@8́tP@@@(˶"@ ƒm+B@ (A@ ƒm+B@ (A@nmB@"u0@nmB@"u0@4hnB@6@4hnB@6@-}4C@0:1@-}4C@0:1@ /6B@J,@ /6B@J,@dXC@`TR'-@dXC@`TR'-@Hv]@@*^A@Hv]@@*^A@-w@@@ #}A@-w@@@ #}A@SvA@LV=@SvA@LV=@`lm_zA@*G[=@lm_zA@*G[=@a 1*9@| U0=@'A@е@@fHPYa=@h+?H >@Wc?hXdAH_;@?%l0!8 ,?@u(@mj063a@@Z$2(D@@(˶"@Il ƒm+B@J,@dXC@ (A@%nHv]@@*G[=@lm_zA@*^A@pI2D@;OI2D@;OX4D@)Wx*@X4D@)Wx*@NJ,E@@ҩ+)@J,E@@ҩ+)@OwocE@ӏ%@wocE@ӏ%@K\?)cE@X7hY0@\?)cE@X7hY0@aۢvE@ <; $@aۢvE@ <; $@-p"\sD@3~Y@p"\sD@3~Y@D/f,E@BNvlB@D/f,E@BNvlB@/jE@YˋD@/jE@YˋD@F@{\B@F@{\B@:DF@ Į"[E@:DF@ Į"[E@PbF@ǎЕB@PbF@ǎЕB@_bF@U!ѕB@bF@U!ѕB@pQ2F@N4[@E@pQ2F@N4[@E@ u=bG@4%^C@ u=bG@4%^C@؃m3G@b*D@؃m3G@b*D@~؃m@G@` g2bC@؃m@G@` g2bC@~;G@cs2H@~;G@cs2H@ kHG@TuY=@kHG@TuY=@]E G@[rI=@E G@[rI=@aK_G@(v0D@aK_G@(v0D@`B5H@I]'P<@`B5H@I]'P<@GHH@&WA=@GHH@&WA=@^@wXH@xO.G@@wXH@xO.G@ΒJH@b7D@ΒJH@b7D@,NqH@(C@,NqH@(C@wg G@K7 @wg G@K7 @qI2D@;OaۢvE@X7hY0@sD/f,E@ǎЕB@bF@ Į"[E@upQ2F@4%^C@؃m@G@cs2H@qwkHG@K7 @,NqH@xO.G@My U@ }H@ U@ }H@38e BV@XB6@8e BV@XB6@:laW@44@laW@44@N +W@u> 6@N +W@u> 6@oTX@7LB<@oTX@7LB<@CV@ 2ߐ;@CV@ 2ߐ;@LV@e;@LV@e;@ 4V@xL;@ 4V@xL;@*<˘uS@L;A@*<˘uS@L;A@9T@,E@9T@,E@*GT@hj.>@*GT@hj.>@.mrT@Л@.mrT@Л@¶U@c H@¶U@c H@2+qW@:9=I@+qW@:9=I@5V8W@U.I@V8W@U.I@4œ9.ՎX@n:96I@œ9.ՎX@n:96I@|L@eQ@|L@eQ@PG1}N@J@PG1}N@J@ mKP@9>VW-K@ mKP@9>VW-K@4_mX@Y#@4_mX@Y#@b }Y@a(@b }Y@a(@da/WlY@Ӗ1j4@da/WlY@Ӗ1j4@A 5 Y@@^̏@A 5 Y@@^̏@4oY@__(@4oY@__(@n̕eZ@'A$@n̕eZ@'A$@DkY@}xv6@DkY@}xv6@r@ 5KY@0A?o5@r@ 5KY@0A?o5@ _oN@<]I=@_oN@<]I=@[yN@&W9@[yN@&W9@ 0P@…oB@ 0P@…oB@¶P@D)B@¶P@D)B@He'N Q@Ԟ$7@He'N Q@Ԟ$7@6Q@pW"E@6Q@pW"E@03 N@|5A@03 N@|5A@ a^@#R#*@a^@#R#*@y K]@0"@y K]@0"@ x^@X'@ x^@X'@|^@ؚ 72@|^@ؚ 72@ZB^@xOI$@ZB^@xOI$@ p\@py@ p\@py@D)`_@Hj LS(@)`_@Hj LS(@>D¶_@04cT @>D¶_@04cT @d=K^@Ԟu6@d=K^@Ԟu6@ Qyq+]@YeH@Qyq+]@YeH@JC#v]@#II@JC#v]@#II@jVp_@C@jVp_@C@zU^@8BJ@zU^@8BJ@8]D_@KJ@8]D_@KJ@`xZ@o3I@`xZ@o3I@J?/_@<B@J?/_@<B@'0CLY@HÝ I@0CLY@HÝ I@ڧSs.^@BJ@ڧSs.^@BJ@pNZ@j,@pNZ@j,@s~Z@ Ug,@s~Z@ Ug,@&f'6[@`h5@&f'6[@`h5@zi^[@p22@zi^[@p22@`UݞQ@d^GD@UݞQ@d^GD@=i9kR@-2C@=i9kR@-2C@M\R@ {<µB@M\R@ {<µB@B R@")(B@B R@")(B@  U@44@oTX@ }H@}*<˘uS@Л@.mrT@,E@~|L@c H@œ9.ՎX@eQ@4_mX@@^̏@n̕eZ@}xv6@_oN@Ԟ$7@6Q@pW"E@u p\@py@>D¶_@Ԟu6@Q0CLY@<B@J?/_@8BJ@-pNZ@ Ug,@zi^[@`h5@ UݞQ@")(B@B R@d^GD@ʿҡ1^@XJy#ʿҡ1^@XJy#}̍ӹ^@z?0 }̍ӹ^@z?0 =_@7!=_@7!n[PE_@^M"n[PE_@^M"_@?"S_@?"S}vZ`@˩&}vZ`@˩&*9D_@?#J?*9D_@?#J?/dY`@DK"?/dY`@DK"?Ԅe_@doH^I@Ԅe_@doH^I@a4r,`@ceH@4r,`@ceH@1%R`@xVef]H@%R`@xVef]H@04HT`@sE@4HT`@sE@) TzT`@ '2E@ TzT`@ '2E@F{T`@t2E@{T`@t2E@G0 `@!]NC@0 `@!]NC@( AE|`@UF@AE|`@UF@708`@8R{H@708`@8R{H@A7r져`@avG@A7r져`@avG@߉*`@ 6=H@߉*`@ 6=H@n`@A@n`@A@#>a@x~}/B@#>a@x~}/B@}0b@5E@}0b@5E@}za@^_I@}za@^_I@f@jBvQ@f@jBvQ@i?9c@H 9= i?9c@H 9=  d@MX# d@MX#cvi5d@xN03#cvi5d@xN03#Ld@8˜%Ld@8˜%Ld@4{5Ld@4{5dZard@+*-dZard@+*- j0`@n݋j0`@n݋ ?a@  ?a@ Ta@*Ta@*9ڨ<,b@f:reDڨ<,b@f:reD8EIPa@@Clp\<"EIPa@@Clp\<"KIsb@ `iKIsb@ `iɵL-yc@qCɵL-yc@qCQܪc@T{Qܪc@T{ 3sc@7  3sc@7 |馠e@,uD|馠e@,uD-(e@7mB-(e@7mBXVf@*1XVf@*1tew3Wf@)1ew3Wf@)1uZrX`@SF)E@ZrX`@SF)E@(U3G@ԕH@'>3G@gI@68@gI@68@hޱI@ <]8@hޱI@ <]8@qԇ-I@‚>8@ԇ-I@‚>8@pxP@J@CCD@xP@J@CCD@J@H)(vB@J@H)(vB@몚K@U6@몚K@U6@| L@ʂ2:@| L@ʂ2:@xi!L@,"ur9@xi!L@,"ur9@< f2L@A?8@ f2L@A?8@; @P:z|F@ZHŸD@@P:z|F@ZHŸD@|uWteF@oCC@|uWteF@oCC@aKW D@DYH@aKW D@DYH@ܹ(LC@MH@ܹ(LC@MH@lֵF@:9 A@lֵF@:9 A@1ȱK@D@1ȱK@D@أN6M@4 EI@أN6M@4 EI@3<-<@KA L@3<-<@KA L@t:@D@t:@D@ `<@<`<@<5F>@ /5F>@ /\01?@Fb@6\01?@Fb@6Q͖x?@<1ɗ9Q͖x?@<1ɗ9U, @@ʥۻ:U, @@ʥۻ:JFcAj@@E:JFcAj@@E:R>[R@@|J@R>[R@@|J@9S@>K@9S@>K@뎃W@,}fI@뎃W@,}fI@xqb Q@5|K@xqb Q@5|K@xr\5DK@`b[I@xr\5DK@`b[I@rD@a<,@rD@a<,@MJ?8Y@KT(I@J?8Y@KT(I@P:D@rv0bD@P:D@rv0bD@Tw?)e@@L+Tw?)e@@L+S,6C@ &S,6C@ &.yH@x(yH@x(p= ;Q@PHp= ;Q@PH7@GA@@ '@GA@@ 'P4MeE@f2]"@1J@1n83@}ԕH@‚>8@J@'>3G@Y몚K@U6@ f2L@ʂ2:@5t:@:9 A@أN6M@KA L@`<@<JFcAj@@ /rD@a<,@J?8Y@5|K@ɧTw?)e@@PHp= ;Q@ & 1*9@Z$2(D@ (A@qI2D@;O,NqH@cs2H@){|L@py@>D¶_@eQ@ʿҡ1^@,uDf@jBvQ@t:@PHJ?8Y@KA L@d= [}fH18tcj 77@$~0w(@o k+&/7@yѦt.doW#S.doW#S>6^1@zʙS@4KRh;@ڦ*T@8:@lWڨ%>(>S=@D¥<& 4!+;@(ټM@@AN@P<@ON%DQ@=?@DcQ@Jp+<@ģ=Jp+<@ģ=Qpb@"{:Eb@H@ZD(@hU\@{wC92c@PlpV%)d@9df64"d@62 4!d@v2z/<d@pp9@- p!Tb@pFDp!Tb@pFDA%V`@c8A%V`@c85E~d@>U@55E~d@>U@5_U)d@Sq._U)d@Sq.E yJd@RGYTe@zX&:?D|d@C 0: d@/ d@f}VF0 d@f}VF0?We@RE?We@REY.(Ze@8NDxDPf@RzH9Aw$ ie@TNUCw$ ie@TNUCfVf@^O& vd[ߖSӑpc+KS f|HQA,J@Me^Li@Nfffff&JĽIgsKՠATT#M|E 6uS!]6MMRyAB%ԯJ3Z@ Q3@^d:1Dn=@E`68@/`Uy<8@/`Uy<ia076>@bd/I;U, @@ ?9~ |3E@b39L?),VFM#T@uo H@>,VFM#T@1 6W@SS@ 6W@SS@24 )f@6oI2XVf@ACW1e/O@f@[Hh1/O@f@[Hh1@MSf@)1f@p2/0f撂h5kf@|o("0撂h5kf@|o("0 gs{_ d = Q3@`LiS@.dFZ@T/7@yѦt>6^1@lWڨ%>=?@ڦ*T@ʹ@hU\@"{:E<d@PlpV% yJd@RGPf@/fVf@3Z@afPX0@33333Hp= ףQ@<(=D ilF@vH'WZ@ǺPT@4 )f@6oI2f@p2/0fC"0sbyf0!X0nT}f C0nT}f C0%ǝc 5@B&9 c#Jv<6@ec]chtC5@ {cHȰ5@={i@civ5@={i@civ5@XWc&Uϸ6@XWc&Uϸ6@f,P@W[qc3N3@E>qc3N3@>d;OaJ1amQ@5a!rhmQ@Sd;OaJ1amQ@ je7a!rhmQ@TbBadmQ@bBadmQ@K je7aJ1amQ@5aJ1amQ@1%Ja!<8nM@`8M@;[q?`fK@[q?`fK@^Bb$``jWVM@Bb$``jWVM@afC"0 {c#Jv<6@f }O@We Q@VCe3K@(\?`3Q@e95d4aL@6)؟av:yO@AJhc6@/gR:DŽd_j_e685@d;OafK@[q?`!rhmQ@5a'fE@^xK)?Q@ psT`mlJ@ae`(XK@4c'Q`H@(\µ^HfK@ h s:`mw/H@.ܲݣ^,͘bI@2`ܘL@Ù_`!<8nM@`ܘL@Ù_`!<8nM@ 5q`DJ@ 5q`DJ@c'Q`HfK@\?` Y9K@Mq _gbǯ.H@Mq _gbǯ.H@m+1_8pwR IZ _H$c)OR#>ʧ^Ro\X]TR"@+_hD@@ oG8-H]H@sY[:8@sY[:8@2,^x!"mR2,^x!"mR oG8-H]ףp= 9@RaXM(D\@@CTu*Ygm[!Rt Rd]QfqH=8Q%(\µ^iD@- PSP Ac̱H@]6Mi@I_L,~_7>IQF:XCX RIQF:XCX Rn&e:QWQn&e:QWQՐ MnDaIՐ MnDaI5a'fE@^xK)?Q@`gbǯ.H@Mq _!<8nM@m+1_RsYH@i(\µ^gm[!RI_L Ac̱H@E yK[8rR@yK[8rR@ŏ1wm]<;R@bodmZ]h2S@F4[w8Q@4[w8Q@kC4]lIF"Q@$z>Yi>"TR@=I1b\x0ZS@_Vv["ʉS@JH"\P S@g pj[qKfS@K,w]ӭ.S@,w]ӭ.S@<"R[CYx2tS@<"R[CYx2tS@e-}Ƭ[b oS@e-}Ƭ[b oS@;~** @P{u$PG$@ lW(|D(@y&;-@P{u$-Kt<@W 0!@H,4!@iPu@Pؙ(Np@T ok3V1$@p Q %@嚈@p[R^@%!@1/6%@0q. @`.;{K@&q %@f.qL@k%@{ffK@@f|Ha)@͘BL@Z t#@xPTJ@`DG=,@\[}K@Y3V7@ROL@ 4!+;@l 58M@9YB&@@"2D>@ 1*9@ @@]3/@T5US@]3/@T5US@B&@HMmM@p+YE4@NFQ@.o$@M O1S@l`q85@,+MJAT@i6"86@g׉zS@i6"86@g׉zS@B&@F5iMK@C7@ qP@۹4@y\S@(a_8@iS@GE_6@,ԛT@GE_6@,ԛT@\(Zq= ף0R@ Z)\hR@>h_Z76zS@dbX*PMS@MNz1YӇ.oR@"mXGz.S@EY_vQ@(\"X3KvR@AulZ0TR@ulZ0TR@{h"YbR@{h"YbR@;SY@WS@;SY@WS@Hν$ @P|hBI@%gܞ@aTJ@%gܞ@x}J@@!@`.;{K@m !@`YjK@ t#@\[}K@l@+"@:_rG@fé$@"o5G@X۠t(5@UM@2[<@ qP@dp+YE4@cKV)Q@x Ö=@q#Q@cP<@ON%DQ@DK?@DcQ@jP@{ M@ xK?@Q@kC4]lIF"Q@$z>YqKfS@;~*p[R^@%!@;-@0q. @@"2D>@ 4!+;@l 58M@.o$@F5iMK@(a_8@,+MJAT@\(Z_vQ@(\"X*PMS@mHν$ @:_rG@fé$@\[}K@IjP@{ M@ xK?@Q@%fVf@ǺPT@f6@/gR:DŽd_j_ Q@5aR^xK)?Q@!kC4]p[R^@ xK?@,+MJAT@ mKP@9>VW-K@xqb Q@5|K@ mKP@9>VW-K@xqb Q@5|K@xqb Q@@|J@$`R@{OK@R>[R@@|J@9S@>K@t+fP@AG@t+fP@AG@SP@h&dK@P@h&dK@ ُR@,dK@ ُR@,dK@ 0P@D)B@¶P@…oB@AP@u-2A@AP@u-2A@p@mSMP@D)B@UݞQ@^ -EzD@¶P@>C@ ]Q@AsţC@=i9kR@d^GD@Q@eD@r2CR@pW"E@NMTQ@z5>@NMTQ@z5>@gd R@06֨D@gd R@06֨D@KCQ@n3[C@CQ@n3[C@LHe'N Q@0F%@8e BV@Ԟ$7@He'N Q@Ԟ$7@*<˘uS@L;A@B R@L;A@*<˘uS@")(B@*<˘uS@hj.>@*GT@L;A@Pw(}S@@V/Y@.mrT@`"#@JT@dXe:@.p/ V@hj.>@*GT@e;@LV@0.7l>@¶U@c H@뎃W@,}fI@8e BV@44@laW@Kg-6@*~,gV@u> 6@N +W@@fOr:@tf1hW@44@N +W@u> 6@LV@xL;@ 4V@ȱ`<<@ 4V@xL;@CV@dK<@ 4V@$L4*:@6W@ 2ߐ;@E?̦V@b|7@E?̦V@b|7@oz*W@ SI@oz*W@ SI@T뎃W@:9=I@+qW@,}fI@7뎃W@:9=I@+qW@,}fI@8{YV@9f;@{YV@9f;@b=i9kR@ {<µB@M\R@-2C@4=i9kR@-2C@9T@,E@m \(T@g>f(@m \(T@g>f(@ZWpT@c7@ZWpT@c7@ s?b U@xta.<@ s?b U@xta.<@J6qGX@K3@6qGX@K3@CV@ 2ߐ;@oTX@qs=@N +W@u> 6@26YX@7LB<@~ [X@!W{H@~ [X@!W{H@ V8W@b4H@œ9.ՎX@n:96I@laW@Y#@{[PX@44@ mKP@AG@9S@{OK@ 0P@z5>@M\R@pW"E@He'N Q@@V/Y@8e BV@")(B@q¶U@44@N +W@,}fI@M{YV@b|7@+qW@,}fI@)=i9kR@g>f(@ s?b U@,E@CV@Y#@{[PX@n:96I@V8W@:H@œ9.ՎX@n:96I@'@tX@n:96I@J?8Y@ݨJ@La4@X@ܫcꉕN@La4@X@ܫcꉕN@J?8Y@HÝ I@0CLY@KT(I@)J?8Y@HÝ I@0CLY@KT(I@jFY@EcOqI@jFY@EcOqI@ R>[R@@|J@9S@>K@zb!S@ }H@ U@>K@pS@,E@ U@ }H@¶U@wD@}u]@YeH@`xZ@aq H@Qyq+]@o3I@6Q@pW"E@9T@/E@ Qyq+]@L@OgH@JC#v]@#II@*XkU ^@BJ@zU^@8BJ@3zU^@KJ@8]D_@0|J@zU^@KJ@8]D_@гYJ@8]D_@doH^I@Ԅe_@KJ@25]@#II@z6>/^@BJ@ s ]@#II@>/^@BJ@__@zJ@__@zJ@G]@|~I@]@|~I@` 3.a@}"R@U"b@S@խ{a@R%MR@6yX5Wf@c_b@[R@>c_b@[R@-70-a@VR@70-a@VR@. hޱI@‚>8@ԇ-I@ <]8@Nxi!L@A?8@ f2L@,"ur9@' U@ }H@¶U@c H@!+qW@U.I@V8W@:9=I@"gI@#Ok8@hޱI@68@M\R@")(B@B R@ {<µB@| L@,"ur9@*>L@@ *[e:@| L@,"ur9@xi!L@ʂ2:@=?@ЬL)E@f@[ΥlS@0CLY@HÝ I@`xZ@o3I@.0CLY@HÝ I@`xZ@o3I@6>3Y@#}G@>3Y@#}G@#b Z@`I@b Z@`I@SV8W@:H@0CLY@ܫcꉕN@6Q@wD@}u]@>K@uQyq+]@L@OgH@Ԅe_@0|J@Q3.a@8 F@f@S@-=?@‚>8@f@[ΥlS@ 0CLY@#}G@`xZ@o3I@W@$|Q>~u1Z@e)V@b }Y@@/ 7@A 5 Y@`@@b }Y@ ?R(Z@a(@c dWZ@p[T%!BZ'\@Pk}k/W<[@j@mT ʿ]@TDڌ@_@κ&@}1>_@κ&@!?0^@.@!?0^@.@!R%?^@:(7@R%?^@:(7@<&s\@`@ p\@py@~r #]@9Ϗ^5N\dO_@?#J?Qp~]@R} !}̍ӹ^@z?0 |up^@ȋvGP$n[PE_@7!W@p[T%!T ʿ]@`@@oTX@ Ug,@&f'6[@s^B@yXX@@^̏@pNZ@Ӗ1j4@U ?7.HY@ab4o/#\@!2,@1 n̕eZ@TDڌ@ #]@`h5@ &f'6[@Jȋ#@jVp_@ܓM#yD@!?0^@l[3m@}1>_@:(7@&s\@ȋvGP$^5N\dO_@py@ 4r,`@xVef]H@%R`@ceH@ 4HT`@t2E@{T`@sE@.4HT`@ '2E@ TzT`@sE@- TzT`@t2E@{T`@ '2E@jVp_@C@ TzT`@)(!~E@Ԅe_@ceH@4r,`@doH^I@SԄe_@ceH@4r,`@doH^I@TjVp_@vYB@J?/_@C@Z gI<_@<]7 I@Z gI<_@<]7 I@12_@!]NC@ZrX`@t)(#E@4HT`@sE@T;i`@dFFF@ TzT`@SF)E@ZrX`@ '2E@{T`@ЬL)E@X`@t2E@ZrX`@BIM7)E@(U3G@ԕH@xO.G@\$-KH@'>3G@ԕH@xO.G@]XAnRH@+hG@XAnRH@+hG@RBK@0 TD@6Q@7OgF@SvA@LV=@A@E 4}?@{Д~"A@@@XA@)&RA@FG&A@j >V(@@jkeC@x@@jkeC@@@CC@x@@>jkeC@x@@D/f,E@BNvlB@ ƒm+B@ (A@D/f,E@BNvlB@v'A@е@@x)MNB@)&RA@w&6?@p= J@485R4@@H$J@e,7?@͘J@485R4@@H$J@f7ւA@uII@jjZ_A@HﺑI@L7ւA@uII@T_A@HﺑI@K{GE@[%X@wg G@f2]"@a{GE@3@wg G@f2]"@`ܹ(LC@DYH@aKW D@MH@'L&q=@g"D.F@>C@G@|A@)&RA@ ƒm+B@ (A@(yuVB@E@=C@ǵG@>C@G@4xh C@ДAsG@I4xh C@ДAsG@LqC@H^G@J485R4@@HﺑI@7ւA@:*J@O6A@$Ye4H@ܹ(LC@uII@Nk"VI@w~Q@;6Q@.#VW-K@PG1}N@J@ mKP@9>VW-K@4xh C@ДAsG@LqC@H^G@Pܹ(LC@DYH@aKW D@MH@ecZC@H^G@aKW D@DYH@M=C@rv0bD@Tߔ~D@E@=C@E@ D@6E@rP:D@YˋD@/jE@.OgD@S@>@oGVQ@S@>@oGVQ@R%oG@L.H@R%oG@L.H@U1ȱK@…oB@9 P@(P2`E@_oN@&W9@(FcO@<]I=@_oN@XQ=@B R@")(B@03 N@f$A@ 0P@*xB@[yN@Ԟ$7@He'N Q@Ll9@hE*rP@D@hE*rP@D@M ~;G@H)(vB@أN6M@xI@(t5L@<]I=@ a_O@XCR@)[5RKH@0 TD@6Q@xO.G@+SvA@LV=@D/f,E@BNvlB@-&6?@3@wg G@H$J@/L&q=@)&RA@=C@H^G@a1485R4@@ДAsG@;6Q@.#@YˋD@R%oG@oGVQ@51ȱK@Ԟ$7@B R@(P2`E@6E@:K]{@@E@:K]{@@<4?F@Ʈ!B@4?F@Ʈ!B@VF@ǎЕB@PbF@{\B@bD/f,E@P1B@PbF@@\QPB@F@U!ѕB@bF@{\B@c9~. F@{\B@|uWteF@oCC@/jE@oCC@|uWteF@YˋD@n/jE@YˋD@@P:z|F@ZHŸD@s _F@xIB@ _F@xIB@ $͓F@8 D@$͓F@8 D@]:DF@N4[@E@pQ2F@ Į"[E@qE G@I]'P<@`B5H@[rI=@E G@&WA=@GHH@[rI=@@aK_G@(v0D@ΒJH@|Qq D@aK_G@(v0D@ΒJH@b7D@CC@TuY=@kHG@@@8YE@h$@D^yH@w&@PbF@:9 A@lֵF@ǎЕB@APbF@ǎЕB@bF@U!ѕB@bbF@:9 A@lֵF@U!ѕB@!VF@&WA=@GHH@:9 A@HpG@H/@HpG@H/@F7E@E@7E@E@_ D@XKGE@:DF@6E@q D@x0b GE@:DF@6E@rHF@)&E@HF@)&E@PNE@Mg'}E@NE@Mg'}E@|uWteF@4%^C@ u=bG@ZoC@|uWteF@4%^C@ u=bG@oCC@@P:z|F@` g2bC@؃m@G@ZHŸD@g@P:z|F@ز:?D@1vQG@b*D@K@G@: Û#D@K@G@: Û#D@$H@v}uD@$H@v}uD@Fa6ŋF@˔C@Fa6ŋF@˔C@cD/f,E@:K]{@@PbF@{\B@:/jE@xIB@$͓F@ZHŸD@<:DF@I]'P<@ΒJH@ Į"[E@e>CC@h$@D^yH@U!ѕB@A@ D@E@HF@6E@B|uWteF@4%^C@$H@b*D@CnLOXC@ۧ1I2D@;OtJk>@,P'Kά@@ffffff/x]>x>@NOH >@ILM K@Z8OH >@ILiV4@NOx]>x>@Z8O }D@;Op"\sD@3~Y@Adb(?@NCv"063a@@?%l0!d%A@G`` @p"\sD@/$@WL>@@8 ,?@Ր @ŏ1w>@Ր @J0Zw>@ @J0Zw>@ @q= ףA@@@Pw`@@ffffff}RA@@9]@@@(˶"@ /6B@J,@B S,6C@ &2(D@Z$S,6C@ &h(D@@cZ$^C@Z$2(D@ۧ12(D@@cZ$h(D@Z$2(D@@cZ$h(D@iZ$rD@)Wx*@X4D@a<,@4Kά@@ۧ1nLOXC@ffffff063a@@7)c#:^m@@NCv"c063a@@7)c#:^m@@NCv"d4hnB@0:1@-}4C@6@-}4C@@ 7f)@P4MeE@0:1@rD@)Wx*@X4D@a<,@7X4D@@ҩ+)@J,E@)Wx*@5NFcPA@X7hY0@\?)cE@*G[=@DA@E 4}?@r\A@|!Tn2@@r\A@|!Tn2@@G&A@QZ@@|/$A^)B@J,@nmB@"u0@,E_)B@J,@nmB@"u0@ /6B@"uqm,@dXC@h:-@E(rGE@ <; $@aۢvE@ӏ%@3_khE@ #@_khE@ #@aۢvE@f2]"@{GE@ <; $@2aۢvE@f2]"@{GE@ <; $@$ F@/!@$ F@/!@n=F@Ibn#@n=F@Ibn#@ ?tP G@@?tP G@@6p"\sD@3~Y@wg G@K7 @wg G@K7 @^yH@^!m<&@I2D@;O0"I@X4 (@tJk>@NCv"p"\sD@/$@GWL>@ffffff /6B@J,@I063a@@ &X4D@a<,@iKNFcPA@@ҩ+)@P4MeE@*G[=@EMDA@"uqm,@dXC@QZ@@!O(rGE@f2]"@{GE@ӏ%@PI2D@;O0"I@X4 (@R:D@ӏ%@wocE@@ҩ+)@CJ,E@A,(@P4MeE@@ 7f)@6GE@6p'@GE@6p'@3@wlME@,)@1J@7I0@Uo[E@w&@8aFcE@@ 7f)@wocE@ӏ%@8YE@w&@1JdK@D(ݗ4@JdK@D(ݗ4@6EF@ͣ08@6EF@ͣ08@H*H㖗I@c_R9@*H㖗I@c_R9@ec!PK@Y 8@c!PK@Y 8@)몚K@U6@ f2L@A?8@xgU%L@N:@xgU%L@N:@v\?)cE@X7hY0@4c5RJ@1n83@ kHG@$ <@`B5H@TuY=@JRw8_I@ <]8@r\I@A?U:@4c5RJ@7I0@1J@1n83@ 4c5RJ@1n83@.UK@U6@1J@7I0@hnM@A?8@!c(G@0R"?=@!c(G@0R"?=@ xP@J@DCD@1ȱK@j|)E@HfBAAM@6"2C@HfBAAM@6"2C@9J@|5A@03 N@fىC@؃m@G@̔ﺛ"C@,NqH@ 'C@؃m3G@ aaD@aK_G@b*D@|*J@f@@|*J@f@@ ŊLWG@b7D@ԕH@'>3G@ pQ2F@b*D@؃m3G@N4[@E@YpQ2F@b*D@؃m3G@N4[@E@p u=bG@4%^C@؃m@G@` g2bC@okHG@TuY=@E G@t5>@?ԇ-I@ȼS8@| L@ʂ2:@GHH@&W9@[yN@q,Q>@,NqH@XjYB@J@(C@`B5H@68@gI@I]'P<@ԇ-I@#)76@몚K@‚>8@:D@ӏ%@JdK@D(ݗ4@V6EF@U6@ f2L@N:@mX\?)cE@X7hY0@hnM@0R"?=@IZ؃m3G@f@@03 N@'>3G@%\pQ2F@#)76@[yN@N4[@E@^9.^@80?i(@a^@`]*@ x^@$@Jw^@X'@վz^@R@vf'e_@XљJ#@R^@N\ "@_@h'w&@ݕ]@@#R)@"; _@$V2@e'ƻ\@b@e'ƻ\@b@/8@f@[ΥlS@W@ȋvGP$^5N\dO_@ܓM#yD@}jVp_@@Clp\<" YZaa1b@doH^I@9&L&q=@3@B R@.#@ &0"I@QZ@@T:D@ӏ%@[yN@'>3G@_ 1-@!,@T𤮉(@daY[OB@#xn /@~}C@t><,@V;TbB@t><,@V;TbB@ V{Vy"@s)?3u^L#@٥[E@["@s)?&@༽ޡ@rc "@ gN>@9YB&@ @@@h@u'3@KY4'@TBx7@Q !@_D0@Q !@_D0@JH @k&C6#@H @k&C6#@۽_Z#@ܙA@۽_Z#@ܙA@;\щ"@TBx7@KY4'@ gN>@w&@.@0&*@Q 3@c @AQrmR'@_t\,@4cZ+@%!@f@l0؃'-@(@KY4'@}6@k+ݳ-@TBx7@0&*@@f?KJ0@#$E $@\ _P+@ \[p=E@8sN4s2@R7 F@Ыne+@iF@:7+@X0AG@aSn+@F@4AH0@!z@G@Sn+@R7 F@:7+@iF@aMY+@KE7G@30@mG@j@"o5G@ig>0#@XG@ig>0#@ 귦G@#1+@.+GpH@ifé$@X0AG@MY+@4ŎG@X0z(@.+GpH@:.@vVI@f#1+@{Y>GH@6q0@0bH@h{2#@$@#L@{2#@$@#L@\kR~$@:I@kR~$@:I@[$~L'@tOK@$~L'@tOK@()@%(}E@()@%(}E@h*@A G@h*@A G@gd2"@E@d2"@E@{!@5 3F D@{!@5 3F D@Db@JTfI@5C^@x}J@:&@[hF@@`:oG@l,@p]H@@(3cI@l,@(3cI@j@JTfI@mDʾ@`:oG@2 @p]H@#g @Q\cG@#g @Q\cG@ql30@JTfI@j@p׮ͼI@NFGn@Z_SI@NFGn@Z_SI@qB@VJ@qB@VJ@@l:@vQH@Dʾ@CH@@l:@CH@l,@(3cI@K r\@aH@K r\@aH@rrr @)A;I@rr @)A;I@~ @ @༽ޡ@FD5@aK@n𤮉(@(@#xn /@~}C@p@h@s)?KY4'@ܙA@urc @@f?KJ0@ gN>@Qt _P+@ \[p=E@8sN4s2@mG@-v@X0AG@6q0@vVI@ x{!@5 3F D@h*@$@#L@yDb@[hF@#g @x}J@{rr @vQH@Dʾ@VJ@} (`n)@|Q s2@AWx @[֐5@H&@`&9 7@@O3@07@`Ђ6M=!@8́tP@@ :l~(@9@6@4hnB@6@HPYa=@aѦ9 W=@!̶U}@@Qbhd?}@@Qbhd?2f=D@pu9+@f=D@pu9+@5= i5@-]ƽC@= i5@-]ƽC@sdF8@PݞA@dF8@PݞA@x<3@jJ8'9@Ͼl10ԧI5@(21=t8@%8tcj 77@Ͼl1'9@:,aK17@wo*67@wo*6oҐ3;1@w)Z.(Ґ3;1@w)Z.(ڃo^2@(vk5ڃo^2@(vk5}=@UR2}=@UR2N07qg&4@8k0z4B@h"Ó:@$dQ9yD@xՃ7@@)(uA@(=J:@؜c=A@vяc6@yY>gG@0Zی:@f,>EH@s)*;@^oM@ !<@(ټM@ 4!+;@^oM@ !<@(ټM@-j5@VcE@\"ja6@A&xC(E@h?m6@G@яc6@K 6H@w\"ja6@v0bCD@/l)6@A&xC(E@X;{7@P< J@ x:@rlһK@[ :@MqtD@h Ö <@nV!E@ 1);<@o:F@L&q=@DF@ 1*9@ǘ>@!!A@?@ !!A@| U0=@Q= `iA@ '8?@Ö+:@ (A@P:D@&E@=5RvA@zZ?@r\A@xijO)D@@DW=@^ v |=@VDW=@>[EtJk>@,P' |=@ 3=@Wc?/:^m@@@ '@GA@7)c#:^m@@@ '@GA@7)c#e4@nD@-j5@VcE@)\!5@p&zkD@/l)6@v0bCD@/l)6@ D@t:@D@h"Ó:@Ќ{|iD@pu:@D@t:@D@c);@x\H-E@B5@ )D@B5@ )D@t#TF;@eQD@#TF;@eQD@r!A@Y~I@r!A@Y~I@F7@ }I@&6?@ZF J@߸AC@G@߸AC@G@E AcC@H@ AcC@H@R(t{I;@(WL@@Wy '9@"wߍ1H+KC9@Ͼl1H+KC9@E`61Dn=@"wߍ1 H+KC9@}15F>@ /o w;@Ρ3*o w;@Ρ3*n1Dn=@}XZE6\01?@E`6  (`n)@|Qf=D@-]ƽC@UҐ3;1@jJ8}=@%107qg&4@@)(uA@ !<@(ټM@ -j5@v0bCD@ x:@rlһK@ 1*9@| U0=@P:D@DF@ňDW=@@ '@GA@Wc?e4@Ќ{|iD@c);@VcE@}7@G@aKW D@BA\L@Y/7@}XZE6\01?@Wy 5d'&,@N3I@:.@xPTJ@`DG=,@xPTJ@8I{3@V!lK@30@!z@G@4AH0@mG@tJF0@bH@06q0@iLH@30@mG@06q0@bH@k06q0@$!g2G@h?m6@AsOH@rԧg2@ThvH@`6@_/>V)H@t- -@ P@- -@ P@8I{3@z(K@OAH)6@T6K@o#H5@aK@FD5@%jOL@GFD5@ )K@X`6@aK@KOAH)6@:J@X;{7@ )K@Lw_EE5@K)VK@w_EE5@K)VK@ A00@Qz3fO@ A00@Qz3fO@7g.@<G@g.@<G@U:.@ tH@ԧg2@N3I@6q0@iLH@ԧg2@ tH@^S3@WSǥG@^S3@WSǥG@`n]3@P^H@n]3@P^H@fbI*/@0tEF@bI*/@0tEF@xQuD[3@pWJ@QuD[3@pWJ@0h/@wH@0h/@wH@[ ^84@uF@(Bm6@8OPG@{^84@8OPG@яc6@G@zxU:6@ZtE@[mގ<@uF@|0Zی:@DF@fP>@;H@[mގ<@fE@L&e=@o:F@}y8U9@F@y8U9@F@m,ct59@􁬑lhE@,ct59@􁬑lhE@u z;@nV!E@[mގ<@fE@1sALe<@76aG@1sALe<@76aG@]h?m6@K 6H@`6@ThvH@s0Zی:@DF@1);<@K@H@y\"ja6@A&xC(E@W{k6@uF@>,&6@;H@3&8@ I@pLA4@VcE@Bi5@;pΈҢE@=vq 4@Ӄ ~D@e4@c*E@Dvq 4@U+~D@:3P4@c*E@Ep+YE4@ qP@C7@NFQ@#H5@%jOL@"P8@ROL@#H5@rlһK@4H~:@ѝ/L@2[<@^ĕPs@N@?K 6>@AN@XJ\)r<@AN@˖?@ON%DQ@b3<-<@+J@PaKX@@KA L@]f37@ I@C077@:J@qd'&,@!z@G@`6@V!lK@- -@:J@X;{7@ P@ɕg.@0tEF@n]3@pWJ@^84@nV!E@fP>@;H@LA4@Ӄ ~D@1);<@ I@]p+YE4@ I@PaKX@@NFQ@9(96@`Ђ6M=!@07@H&@` q.@z @(96@H&@%k0,@|k3t'@-@Q)@<k0,@|k3t'@-@Q)@;07@u(@XdAH_;@`Ђ6M=!@)~.@Ф0@``Yv/@P ur4@k+ݳ-@@O3@+p7@bhh7@dj7,@#$E $@KJ0@z @KJ0@#$E $@ s2@]^X @# s2@AWx @XdAH_;@N;@$HPYa=@@tWWiV4@:v |=@ 3=@Wc?V xK=@Ր @ŏ1w>@p\9g@X xK=@ @J0Zw>@p\9g@^XdAH_;@M &@ xK=@u(@:>@@8 ,?@ @+p7@@O3@ 9@6@%78@6@ 1*9@|г?@HPYa=@!̶DW=@^ v9 W=@ر=@aѦU-Gq>@ s 9ԧq>@mA 9 W=@aѦP\0=@h+?icjk=@mA -Gq>@رcjk=@ s 9ԧq>@رP\0=@h+?hCK>@NOP\0=@h+?8̒>@Z8O( 8=@^'_ ( 8=@^'_ NΉ3Rx>@ףp= Ή3Rx>@ףp= ٸ~>@@T$* ٸ~>@@T$*  `m⒘!@0m^ @@0&@s)?n$@)x)?$@)x)?@@0&@&0v'@0m^ X0v'@(0B[](@&$0B[](@Эg~fw(@(WM (@I+GM (@I+G%.v@X@} .>@XGtJk>@ILib07>@,P'Z0)=@< 㣣jZ0)=@< 㣣jy=@)?=@)?1 3=@Wc?8 ,?@@WY>@ne@Y>@ne@݈>@Q@ ݈>@Q@ XAd>@VqB@Ad>@VqB@iLy*@j[@iLy*@j[@$C\8+@I/ .@C\8+@I/ .@B1.@P ur4@xk.@0LN5@RB1.@P ur4@xk.@0LN5@HH>:e.@`c-5@H>:e.@`c-5@BK}2@p;@K}2@p;@dc-@ )@dc-@ )@V&M2@.Sl.@&M2@.Sl.@Cw 4@u>~@w 4@u>~@P/h=@,c(B.@/h=@,c(B.@2a@>@{d :@2a@>@{d :@{t8>@0Q@t8>@0Q@Wdj7,@#$E $@XdAH_;@bhh7@KJ0@@tWW8 ,?@N;@͢+p7@ s 9ԧq>@|г?@9 W=@ s hCK>@Z8O`m⒘!@Эg~f.v~@2a@>@{d :@eo2@qUF@nDp2@kVF@Z@pj/@33333SE@?$Gpg3@NeF@?$Gpg3@ #nF@g3@BT)nF@<g3@ #nF@g67^3@X9vnF@)6$#g3@BnF@g67^3@X9vnF@4AH0@pdH6F@eo2@!z@G@_3@+j0 E@r߉3@X9vnF@ao2@qUF@%`3@evY>G@ueo2@ {F@@[)3@qUF@nDp2@kVF@%`3@evY>G@[nDp2@ {F@@[)3@kVF@;\UI2@/ɋF@\UI2@/ɋF@:> 63@^n(G@> 63@^n(G@Q33333s2@bp=E@(\2@33333SE@8sN4s2@bp=E@(\2@33333SE@8sN4s2@X SD@;E2,_3@bp=E@}(\2@33333SE@=73@+j0 E@r3@cU8E@r3@cU8E@eG2@̋QHE@eG2@̋QHE@~_ 4@D@~_ 4@D@'+e4@S:XKE@LA4@&1hE@07qg&4@mC@)\!5@p&zkD@Slg^-4@>+:E@Slg^-4@>+:E@&t$v4@p&zkD@)\!5@U+~D@-;jv4@p&zkD@)\!5@Ӄ ~D@?$Gpg3@BT)nF@@007c3@ڿ$FF@g3@ #nF@@007c3@ڿ$FF@=@[)3@ڿ$FF@@007c3@ {F@m~~$/3@Yr nInF@m~~$/3@Yr nInF@+%`3@8OPG@^84@evY>G@v?g4@`PF@?g4@`PF@,s ,ծ23@i"1F@s ,ծ23@i"1F@;|1@>JF@|1@>JF@rs4@A{D@rs4@A{D@?}4@[ $zE@}4@[ $zE@>:3P4@Ӄ ~D@e4@U+~D@C؅N4@goFE@؅N4@goFE@(=73@&1hE@I.!V4@+j0 E@n _M3@X SD@03@~}XE@M3@jMSD@03@~}XE@i׭Q3@mC@07qg&4@X SD@"@W3@Ք\E@"@W3@Ք\E@d03@xD@E@+e4@~}XE@~@pj/@33333SE@r߉3@!z@G@ao2@ {F@%`3@evY>G@33333s2@X SD@r3@+j0 E@e~_ 4@mC@)\!5@&1hE@A|1@`PF@?g4@evY>G@rs4@A{D@؅N4@goFE@=73@mC@I.!V4@+j0 E@ռt{->@ /Tw?)e@@L+\01?@<1ɗ9t;?@Fb@6Q͖x?@ʥۻ:U, @@<1ɗ9uU, @@E:JFcAj@@ʥۻ:5F>@Fb@6W=l@@ /Ӏd?@ęai|:Ӏd?@ęai|:\ #%"A@t]9lC@#%"A@t]9lC@^Lk@@)A@Lk@@)A@YWoTC@$A@WoTC@$A@kP@@|w wA@P@@|w wA@ZݨA@r@@ݨA@r@@@ tWFݞA@ɥ7?@ tWFݞA@ɥ7?@f_A@b?)“?@f_A@b?)“?@c5RzB@ˋwY?@5RzB@ˋwY?@=g:EA@!}>@g:EA@!}>@|lX@@L++@@NCv"׿k`A@AP)׿k`A@AP)$6nn5A@o%%$6nn5A@o%%>R~A@*3 1>R~A@*3 1h-Gq>@?%l0!Adb(?@mA 9ԧq>@?%l0!Adb(?@ s g?"z@@/$@d%A@@(˶"@q= ףA@@d%A@/$@3kocA@2%83kocA@2%8!W96@@.ҥ"W96@@.ҥ"ZTw?)e@@!0BA@@ '@GA@+q'%8cB@M&0%8cB@e6g0'S,6C@מ&%8cB@e6g0'S,6C@0р&1lC@&g6%lC@&g6%" @E:Tw?)e@@L+P@@!}>@WoTC@t]9lC@ilX@@*3 1>R~A@NCv"E-Gq>@.ҥ"d%A@@(˶"@! ?@,gxJ@_0!N?@,gxJ@ʔ~&@@X/XnJ@`0!N?@,gxJ@ʔ~&@@ȅY>nJ@*hlj>?@ZF J@&6?@^J@^hlj>?@ J@,7?@^J@+_!?@Xm"z5J@_!?@Xm"z5J@.&6?@ZF J@,7?@ J@d5/vj?@(5J@5/vj?@(5J@-'r'@@J@'r'@@J@WS.{O;?@7H@S.{O;?@7H@D7@yK@(t{I;@҂LP@I4H~:@A^J@3<-<@aPAM@%)LR#=@^J@ʔ~&@@{%N@S.{O;?@7H@'r'@@(5J@c @s)?FD5@$@#L@y (`n)@jJ8P:D@(ټM@d'&,@Ӄ ~D@PaKX@@NFQ@`m⒘!@Q@ 8 ,?@|г?@ѯ@pj/@mC@)\!5@!z@G@t{->@E:rD@t]9lC@m7@7H@'r'@@҂LP@ xECmUGA$t%@ҊTA*o&@8DU()@(> lT,T.@fV #R-@(> lTdFb0@FT0%@(> lT #R-@R:E|U 6s @cϽTyH-&@Yd>U-43@jdR<4H07@Y>!$CU7)@>!$CU7)@% ULdK#@% ULdK#@ҊTnj!#@R#T0%@0 DR@] @__zMXSS(@NQPƱ֭;2@NQPƱ֭;2@&PSQ1@^zeP4F2@[P @[P @CɜQSU|!2@ɜQSU|!2@i Ts @i Ts @N$@(\rNHz%@ͰGNxF$@ͰGNxF$@:cϽT 6s @R#Tnj!#@ RiK|Y%_H@RiK|Y%_H@FMMtN.$OG@tnRJmvI@3^I SR:N@_)j(VIT@w $:O˸KH@ $:O˸KH@ŬC9!Pb(H@̒5NQ1߄H@1I PR~RF@wb֋O*G@0KӠxO7o5G@KӠxO7o5G@Q"QUQ@Q"QUQ@CL-DJTRS@CL-DJTRS@X`:̀S8:XzO@6{.8+P@B:1j0+@ v0 #"0@["<1 #"0@Aw%0X4@["<1X4@@@::1l5@["<1X4@wi^!8|Oe;@NP3&e"GP@NP3&e"GP@;~*Plc$@³&`4c\!@n#'i$@T |D(@³&Plc$@P{u$, @³&p%Kl@O@Plc$@ RaXGz9@KPBF@|n(;V H/@eVq2@ ("QqD@RkmE@_*VVQ'',@fVu t/@GQX/gYs0Q@#ݛsWz)/Q@9 lW(;-@Y}8@䓔HFH@Ռ?hGQM@p*VV*@pnMUQ'',@_FVx,~x+@*VVQ'',@` (\rSL9@@S ףp= ;@-$%zMS@D:@$%zMS@D:@0F͌qS~E:@0F͌qS~E:@QSQk:@GztSQ:@,%zSH^y8@%zSH^y8@|DL$S(\µ7@7bSKY859@+G'o R2@G'o R2@Qy2@Qy2@ԑiSyV5@ԑiSyV5@fV 6s @jdR<4H07@qcϽT @__zM4F2@M^I SRR~RF@_)j(VIT@)X`:̀S8j0+@wi^!P@;~*p%Kl@O@|D(@QX*@Ռ?z)/Q@ԑiSy2@Q ףp= ;@vӿXF?*R@vӿXF?*R@QlXt[rB0XQ@QlXt[rB0XQ@߱jIXL0nS@߱jIXL0nS@ѯWv?*FR@ѯWv?*FR@WjVjS@WjVjS@q]WySR@q]WySR@ kWPX-@:(IVMZ1@04 V3#ټ/@04 V3#ټ/@8"'NVL/@|n(;V(Xg1@R 8V[y+@R 8V[y+@-Vp:1@-Vp:1@RqatWC@RqatWC@?lEW+͢L@lEW+͢L@z4V7S@4V7S@5EW%VR@5EW%VR@d%Sek.T@d%Sek.T@ 1UU()@8DU*@:КUyH-&@xECmUu !*@#)UDnt-@#)UDnt-@91w-!OSHQP@nRcE aQ@8S('O@S('O@KlTL$JO@KlTL$JO@|eF/TC- P@|eF/TC- P@kE/ SQiP@kE/ SQiP@1͘Sa"NR@1͘Sa"NR@vӿXt[rB0XQ@q]WL0nS@QRqatW[y+@-VC@-lEW+͢L@d%Sek.T@ 1UUyH-&@nRa"NR@nrX {c/pvS@B5W~4ͷS@LG;GX4=R@l'_Sjb1TJS@G'4XNR@`/d7gWlR@DiX:ڏ(r_S@l;nW)juS@ILTo l-X@zǍS@_vOtUQyPT@NDUVްmQf S@NwgT@Oa#(XpA0R@l4rVOR@BҚX!Vd 'N@ケ N$qsR@@ 4U\][O@\a4T%%HoP@7 AJT?o*RaO@: xT̯uO@68T@/R@S?pR@?ŏ1w-T7N@ۧ1S6>W[1O@5tWx,~x+@FVPX-@FVxF'KL*@1UUx,~x+@+IV(Xg1@:3Vq2@|n(;VxHyi/@fV H/@9t7*wXq|$2@aU >]'9@ &3(Q\rKKToPπRtQJP&3(QK33333CPπRtQJ!8I PQU1'K!8I PQU1'K:fR[rPKKToP'†WBJQښZR-T&JP貋e6v .XQBv .XQBEC+Id6P6(DBEC+Id6P6(DB=R1@OJ3$QhY2*:ۦQhq K*:ۦQhq K xvVS—, G!@Ϋ)Qxk3(@W_Y1SpMZ|1@<4 SH0.32@R#TP4cLW!@xvVS$9#@.;lPR|K#2@vTQBj3@ycϽTݠ@rJkxS`_1!@S|Q HV 2@+;Q)3@FzVTQ;1@2.YQdI3@[|SSR2{ ?@P(͆u'@rJkxS+K$@o.5OSP4cLW!@nrX4=R@NwgT@a#(X7N@ケ NOR@yt7*wXxF'KL*@aU >]'9@U=R[rPKEC+Id6PhY21cϽT2{ ?@PBj3@ › EL#*?B%+CKUԢ0@G KK$EA @G KK$EA @B%+CKJ㝪~@PK<Ͷ@(E2ߒL V̏@D&4J1p@1+J^!v@1+J^!v@(k9ؗQhY2 eQڨ{1mRQW1@P L@V eQb2w67LPڨ{1"?aQ0E<-4L$ʿLH D##7LPuO6dWO:L5dWO,a?69vXLM ?W3!dWO vnj;Ԭ PK,a?6"<8΄Nw@__zM@] @__zMpg@j@(E2ߒL@] @x9vXLM̤?9>%K0E<-4I_L,~_7> 'mJG29.I_L3Z@NӚJg>3̥ PB03̥ PB0H>]yM< @>]yM< @zo9L_@zo9L_@=m㵷K6`YM@m㵷K6`YM@Z+ȦKt wn%Z+ȦKt wn%̫/ Mbcl7̫/ Mbcl7OB%+CKJ㝪~@hyxK<Ͷ@ 1vC=K<Ͷ@D&4J V̏@D&4Jlc@1!2I V̏@B%ԯJ3Z@zlp]A_@/uF]NgJ?› EL@.GZ@lMAqd?› ELpg@j@@P i.?ZpLNw@B%+CKaOWm@1!2Ilc@*"R( *"R( [ K R呠JJ@[ K R呠JJ@LZThY2(k9ؗQ~b%= BRW1Q1yQG J\y.R!9=y?&"?aQۂK eQڨ{1O@*QhI%=T~b%= 7SA8ކC?]\{kSA8ކC?$,HS+K$@X7S˧uÿBRA8ކC?e5;T %9PR˧uÿ\bS^bS^(E2ߒL#*?1+J1p@(k9ؗQb2w6L$ʿL L@dWO3Z@NӚJ@] @}3̥ P_@Z+ȦK< @Y@P3Z@zlp]Apg@j@5LZThY2O@*Q呠JJ@ =T %9PR+K$@ @@::1 ;k5@_'T! ;@@@::1l5@L%[8KJGA@@r D#6ީI@?!,3ؐK@wi^!}8@Y8|Oe;@i%Y! ;@mY)xA@Z_-r) n rUJ@Z_-r) n rUJ@05c(n0?큃M*@a\0j0+@805c(n0|D (@̉}m0?큃M*@OEu٤.r*@Eu٤.r*@7̉}m0hBװ&@'B.|D (@ W Np@NTi$@w‹Z&kPz$@w‹Z&kPz$@x o9sj$@x o9sj$@D="XwM@D="XwM@-s'<(%!@-s'<(%!@hˤf+q(@n#'Y, ,)@'B.`4c\!@;~*hBװ&@ue $@e $@t;Q0@t;Q0@IO #l0bLkB@`XnQqD@GSq 2IC@GSq 2IC@'G"hMB@mLHe#E@(Ps"<@(Ps"<@ wi^!8|Oe;@ 2T! ;@hѦ)Κ8@Ѧ)Κ8@$24G4@$24G4@5E(9.Ԋ(@5E(9.Ԋ(@Nxk-a1a,@Nxk-a1a,@I05c(n0љB*@; +x+@R'B.hBװ&@hˤf+Y, ,)@̉}m0|D (@hˤf+;큟A)@Q v0;-@ lW('0@dIX^ J@`եF K@odIX^ J@4tV,3ؐK@b>P&ū)LD@b>P&ū)LD@ φ^6{`J@ φ^6{`J@KP"<DK@KP"<DK@@@::1 ;k5@mY),3ؐK@05c(n0hBװ&@'B.j0+@'B.XwM@e;Q0@]Ѧ)24G4@mLHe#E@905c(n0hBװ&@ lW('0@dIP&ū)LD@ φ^F K@ O@KhZ@*XR @U= __?|D%@Fd8?@y &@>?`0ɑ@ao?|D%@@w^|{`E@֌-@pi@I@jRkj+E@A[gnS@|{`E@k`Xn(j0b2A@P@{>@rc "@^$yB@Fd8?|D%@ƚ\<@prX['@O+B?`"%!@ao?`0ɑ@ao?`0ɑ@嚈@lc @(֌-@pi@I@Hν$ @6یBI@k)] @0~5I@l30@P|hBI@Hν$ @0~5I@l30@P|hBI@A[gnS@{>@:vRdj7,@#$E $@XdAH_;@H&@UЫG'@ b1>1=t8@yѦt0ԧI5@}1+@@Wy rk0,@|k3t'@-@Q)@[3u+@z @`&9 7@bhh7@GR<@^ĕPs@N@?K 6>@AN@9 W=@+q'2(D@ffffff%lX@@!0BA@NCv":^m@@@ '@GA@7)c#0v'@( (`n)@2)𤮉(@daY[OB@#xn /@~}C@+e4@c*E@vq 4@S:XKE@*;jv4@p&zkD@/l)6@A&xC(E@xxՃ7@@)(uA@(=J:@؜c=A@07qg&4@8k0z4B@pu:@D@w6$#g3@BnF@g67^3@X9vnF@/?$Gpg3@ #nF@@007c3@ڿ$FF@? _P+@ \[p=E@@007c3@!z@G@|@pj/@33333SE@r߉3@NeF@33333s2@bp=E@(\2@33333SE@ _M3@jMSD@03@~}XE@X0z(@{Y>GH@ԧg2@vVI@`s)*;@^oM@ !<@(ټM@Y3V7@(WL@ !<@l 58M@{PjMk;@BA\L@ !<@^oM@"#H5@rlһK@3<-<@.L@ hlj>?@ZF J@,7?@^J@20!N?@,gxJ@ʔ~&@@X/XnJ@1\"ja6@ D@[mގ<@uF@y[ :@MqtD@h Ö <@x\H-E@^84@ZtE@L&e=@f,>EH@q&6?@p= J@485R4@@H$J@[f37@ }I@PaKX@@KA L@Xh?m6@g"D.F@aKW D@:*J@H`m⒘!@& 1*9@AsOH@5ЫG'@}1+@@bhh7@70v'@!02(D@AN@i9𤮉(@@)(uA@pu:@ڿ$FF@E;X0z(@jMSD@03@vVI@!=#H5@ZF J@ʔ~&@@l 58M@>^84@MqtD@aKW D@KA L@@ E_)B@A,(@P4MeE@0:1@::D@ӏ%@8aFcE@@ 7f)@7aۢvE@f2]"@{GE@ <; $@#PbF@:9 A@lֵF@U!ѕB@rD@)Wx*@X4D@a<,@9Pw`@@ۧ1p"\sD@/$@Rw8_I@#Ok8@r\I@A?U:@i }D@;O0"I@X4 (@;(rGE@[%X@D^yH@w&@$NFcPA@X7hY0@.UK@@@LkHG@$ <@`B5H@t5>@@wlME@,)@1J@1n83@Kԇ-I@#)76@ f2L@ʂ2:@-4c5RJ@7I0@hnM@A?8@| L@,"ur9@*>L@@ *[e:@zdXC@a<,@rD@`TR'-@]_oN@Ԟ$7@*<˘uS@")(B@He'N Q@0F%@26YX@L;A@Pw(}S@@V/Y@.mrT@`"#@ ~,gV@44@N +W@@fOr:@tf1hW@Y#@r@ 5KY@nV<@JT@dXe:@.p/ V@0.7l>@O 4V@$L4*:@6W@dK<@g{GE@3@wg G@f2]"@c dWZ@p[T%!BZ'\@Pkw^/]@`',"ڛ]@x!0 1]@m$ʿҡ1^@X˜"Qp~]@R} !}̍ӹ^@z?0 k/W<[@j@mT ʿ]@G9@W@$|Q>~u1Z@e)V@XX@@/ 7@ަβeZ@Ӗ1j4@=b }Y@ ?R(Z@`@@da/WlY@Sa\+@9.!Z@}xv6@ DkY@z3!@ WuU[@ Z7@IBGY@'A$@@TZ@#$-@v}([@p22@֪9[@^4@V)H@jOQ @P tC@`7#@3D@嚈@p[R^@l0؃'-@4cZ+@^I SR:N@_)j(VIT@䓔HFH@Ռ?hGQM@ ("(j0b2A@P@mE@@w^j+E@2 @pi@I@)] @0~5I@l30@P|hBI@FMMtN.$OG@tnRJmvI@eo2@ {F@@[)3@kVF@>@l:@vQH@@(3cI@vDb@ 귦G@:.@\[}K@_:&@sF@fé$@XG@u3@ gN>@9YB&@(̬B@@0q. @`YjK@&q %@f.qL@`@+"@KE7G@06q0@0bH@k\y.R3Z@zlp]A@.GZ@ eQb2w6L$ʿLH D##MdWO vnj;>%K ?W3TB%+CKJ㝪~@PK<Ͷ@ lM#*?D&4J1p@ <8΄NgJ?› EL@] @|SSR i.?__zMXSS(@HjP@{ M@ xK?@Q@.o$@M O1S@l`q85@,+MJAT@B&@F5iMK@C7@NFQ@<d'&,@;H@3&8@V!lK@>6^1@zʙS@4KRh;@ڦ*T@8I{3@z(K@X`6@aK@!p+YE4@UM@˖?@q#Q@۹4@y\S@(a_8@iS@#H5@P< J@ x:@ѝ/L@EP<@ON%DQ@DK?@DcQ@8:@lWڨ%>(>S=@D¥<Vx<3@^d:1Dn=@Ͼl1sdAw'@ d ='9@$~0 @@::1KhZ@@LHe#E@QX`:̀S8Plc$@P{u$P@qSB:1`"%!@KY4'@F K@MU嚈@p[R^@`6@͘BL@)W^I SR(j0b2A@@[)3@VIT@Y@l:@ gN>@06q0@f.qL@Z\y.R3Z@zlp]AXSS(@\jP@;H@ xK?@ڦ*T@^dAw'@lWڨ%>˖?@iS@u`cϽTݠ@o.5OS$9#@=T %9PRA8ކC?\{kSW1@Pxk3(@ao2@qUF@%`3@evY>G@UGz.Q@33333Hp= ףQ@PHF @@f?KJ0@Q)@( V{Vy"@s)?&@٥[E@@hU\@{wC92c@PlpV%?AQrmR'@``Yv/@TBx7@N>?`0ɑ@A*d|`@|Dx(@.]X8#@|j@@ y;.@ppb@"{:Eb@H@ZDfC"0sbyf0!X0 vd[ߖSӑpc+KSm+1_8pwR IZ _H$c)OR>ʧ^Ro\X]TRCTu*Ygm[!Rt Rd]QfqH=8QpݏPFZ@T}f:MHG@0M3@mC@)\!5@~}XE@++e4@nD@Bi5@;pΈҢE@,t$v4@p&zkD@)\!5@U+~D@Cvq 4@Ӄ ~D@e4@c*E@BÖ+:@ (A@|uWteF@&E@bNfffff&JĽI]6MMRyANӚJg>BfPX0@pqhAJFcAj@@E`6m |3E@b39L?)@bd/I;U, @@ ?9acϽT %%`3@evY>G@-d33333H92c@TBx7@ ff[ߖSKToP0!X0gfVf@ V̏@id@RGf@p2/0knDp2@ (A@|uWteF@evY>G@ymNfffff&JL?)V(@@D/f,E@BNvlB@o|uWteF@4%^C@ u=bG@ZoC@hD ilF@fCΦT@TwI@Ӹ:T@6k"VI@w~Q@;6Q@.#VW-K@ mKP@9>VW-K@xqb Q@5|K@R>[R@@|J@9S@>K@¶U@wD@}u]@ݨJ@'~;G@0 TD@ U@{OK@W[5RKH@'>3G@ԕH@xO.G@VaK_G@(v0D@ΒJH@|Qq D@@P:z|F@̔ﺛ"C@i G2I@b*D@jVp_@vYB@ZrX`@)(!~E@pQ2F@b*D@؃m3G@N4[@E@TŊLWG@XjYB@@P^K@{;G@V8W@b4H@œ9.ՎX@n:96I@rZ| X@mzS@'WZ@y@ٔ+S@5J?8Y@HÝ I@0CLY@KT(I@Ԅe_@ceH@4r,`@doH^I@ZV@OeS@(' Y@ǺPT@70CLY@HÝ I@`xZ@o3I@X뎃W@:9=I@+qW@,}fI@YxP@J@f$A@9 P@(P2`E@>BK@D)B@r2CR@7OgF@R>C@t@mSMP@/^@BJ@eվz^@R@vf'e_@XљJ#@ zU^@KJ@8]D_@0|J@Lup^@ȋvGP$n[PE_@7!enݞ_@wD$@yX5Wf@@@8 ,?@ @ xK=@Ր @J0Zw>@p\9g@֐5@`Ђ6M=!@-}4C@6@%8cB@e6g0'S,6C@0р&n2(D@@cZ$h(D@iZ$t{->@E:BBcD@@cZ$l%78@6@4hnB@?@H+KC9@}XZE6W=l@@ /S07@ @d%A@ :l~(@\4xh C@ДAsG@LqC@H^G@IcZC@H^G@aKW D@DYH@^ܹ(LC@DYH@aKW D@MH@W7ւA@uII@T_A@HﺑI@JHv]@@KJ A@6IA@A@] *w @@DR3IA@p8A@yA(A@^Д~"A@Zxީe@@x)MNB@)&RA@D0Zی:@DF@fP>@;H@b |=@ 3=@Wc?5cjk=@ s 9ԧq>@رP\0=@h+?hCK>@Z8O K@NOH >@ILKHPYa=@@tWWib07>@,P'}-Gq>@?%l0!Adb(?@mA \/$A^)B@J,@nmB@"u0@S,6C@ &h(D@Z$&063a@@7)c#:^m@@NCv"_!!A@| U0=@Q= `iA@ '8?@DW=@>[E}RA@@6=5RvA@zZ?@r\A@xijO)D@@!A@LV=@d A@е@@gSvA@tZ2=@CC@x@@A֐5@E:BBcD@?@0Zی:@Zxީe@@aKW D@HﺑI@HPYa=@ &h(D@"u0@͐DW=@>[ECC@x@@ kWx,~x+@fVMZ1@<*VV()@(> lTdFb0@=КUGA$t%@(> lT,T.@R:E|U 6s @R#TA*o&@ QX/gYs0Q@#ݛsWz)/Q@ 4U\][O@\a4T%%HoP@ AJT?o*RaO@: xT̯uO@ŏ1w-T7N@ۧ1S6>W[1O@kC4]lIF"Q@$z>Yi>"TR@\(Zq= ף0R@ Z)\hR@8T@/R@S?pR@Y_vQ@(\"X3KvR@a#(XpA0R@l4rVOR@nrX {c/pvS@B5W~4ͷS@G;GX4=R@l'_Sjb1TJS@'4XNR@`/d7gWlR@LTo l-X@zǍS@_vOtUQyPT@iX:ڏ(r_S@l;nW)juS@DUVްmQf S@NwgT@ҚX!Vd 'N@ケ N$qsR@QSQk:@GztSQ:@(\rSL9@@S ףp= ;@1w-!OSHQP@nRcE aQ@ŬC9!Pb(H@̒5NQ1߄H@I PR~RF@wb֋O*G@@+_Gz9@KP Ac̱H@DYd>U-43@jdR<4H07@|DL$S(\µ7@7bSKY859@_Y1SpMZ|1@<4 SH0.32@.;lPR|K#2@+;QBj3@S|Q;1@2.YQdI3@PSQ1@^zeP4F2@+N$@(\rNHz%@?KRa`q8M@1%Ja!rhmQ@PҺaiD@^xK)?Q@~d;OaJ1amQ@5a!rhmQ@O oG8-H]PX-@aUM(D\@@FVxF'KL*@pnMUQ'',@"'NVL/@eVq2@ kW 6s @R#TMZ1@akC4]7N@SOR@=nrX4=R@NwgT@@+_Gz9@̒5N$qsR@Yd>U$@(\rNKY859@ѝKRaxF'KL*@^xK)?Q@ fm2@"mX*PMS@3`m⒘!@}12(D@AN@BPw`@@m$}̍ӹ^@U!ѕB@O\y.R3Z@˖?@VIT@QbfVf@evY>G@1q(t{I;@C 0f@ǺPT@~r #]@8˜%f@S@9֐5@E:BBcD@HﺑI@KRa 6s @^xKwgT@ fC"0sbyf0!X0Ycm2@MYc*gD4@{ec]chtC5@ {cHȰ5@%ǝc 5@B&9 c#Jv<6@z&xUc ^L@*.Gc)(L@|95dRM@RWdhQz,1N@}.Pye }O@Wev)O@~f,P@W[Yi>"TR@'ŏ1wm]<;R@bodmZ]h2S@0I1b\x0ZS@_Vv["ʉS@4H"\P S@g pj[qKfS@5\(Zq= ף0R@ Z)\hR@(iX:ڏ(r_S@l;nW)juS@3nrX {c/pvS@B5W~4ͷS@6h_Z76zS@dbX*PMS@7 Nz1YӇ.oR@"mXGz.S@/Y_vQ@(\"X3KvR@+QX/gYs0Q@#ݛsWz)/Q@&'4XNR@`/d7gWlR@.a#(XpA0R@l4rVOR@,G;GX4=R@l'_Sjb1TJS@1LTo l-X@zǍS@_vOtUQyPT@8 AJT?o*RaO@: xT̯uO@# 4U\][O@\a4T%%HoP@$fC"0*.Gc Q@ApsT`mw/H@m\`LiS@kC4]lIF"Q@l;nW*PMS@Nz1Y?o*RaO@l'_SQyPT@ժ_Y1SpMZ|1@<4 SH0.32@T(\rSL9@@S ףp= ;@.;lPR;1@2.YQBj3@=PSQ1@^zeP4F2@jN$@(\rNHz%@yDUVްmQf S@NwgT@9Yd>U-43@jdR<4H07@;8T@/R@S?pR@)ŏ1w-T7N@ۧ1S6>W[1O@"QSQk:@GztSQ:@|DL$S(\µ7@7bSKY859@VCe1@OJzlp]A)?Q@ Jhc AJ5@PCce685@ZӼcC5v4@=\rcm25@m+1_8pwR IZ _H$c)OR>ʧ^Ro\X]TRCTu*Ygm[!Rt~u1Z@e)V@Pjc !@DD@@3u#@F.GE@Ac dWZ@p[T%!BZ'\@PkI w^/]@`',"ڛ]@x!0 G~r #]@9Ϗ^5N\dO_@?#J?N1]@m$ʿҡ1^@X˜"FQp~]@R} !}̍ӹ^@z?0 H x^@$@Jw^@X'@cnݞ_@wD$@6^1@zʙS@4KRh;@ڦ*T@].o$@M O1S@l`q85@,+MJAT@\䓔HFH@Ռ?hGQM@CZV@OeS@(' Y@ǺPT@u@r D#6ީI@`եF K@BrZ| X@mzS@'WZ@y@ٔ+S@s@hU\@{wC92c@PlpV%ݸ#Q)za@5D@ YZaa1b@PF@Wխ{a@R%MR@6(>S=@D¥<)Szc@MX#tEd@c|"v|BVd@X#cvi5d@ ',أ w T;*d@8˜%Ld@ؑ/h$ d@9df64"d@62 4ZB:1pqhAf@[ΥlS@k%@{ffK@@f|Ha)@͘BL@< *w @@DR3IA@6IA@A@ v}([@p22@֪9[@^4@:k/W<[@j@m[P]@Y~S@up^@ȋvGP$lg_@r<  yJd@RGYTe@zX&:?D^|d@C 0: d@/Y.(Ze@8NDxDPf@RzH9A_4 )f@6oI2XVf@ACW1>@MSf@)1f@p2/0?>yX5Wf@ Rd]QfqH=8Q:fR[rPK33333CP'†WBJpݏPFZ@T}f:MH@ 4!+;@(ټM@ Aؿ>???T;i`@dFFF@+ o0-0!_^ZAE|`@UF@S$?d= [}f`L t0#-F₩e?3u^L#@٥[E@nFR->4@["@s)?;kv?1|DLc7ۤ4@:C@PCcbE-5@|@ {cyR5@ߊU?B&9 cZd5@ $0@: d@02aw0 H\3}YS|E??2$3K<Ͷ@! G?^w?b2@B%+CKJ㝪~@57> = A?A?A7r져`@avG@ =t\R\߉*`@ 6=H@Qz-3-4>708`@8R{H@۾`@U`@lֵF@:9 A@cr_3_[ @bF@U!ѕB@ )FG?`B5H@I]'P<@@8ۨ,A@E G@[rI=@s12$>{@O+B?`"%!@(V|85>?ao?`0ɑ@V ֿ:NQ?@@::1l5@M/D}?/?["<1X4@0=:J= ? ?@GA@@ 'dcr%q$q+ @~ @:^m@@7)c#XOHt$@Y}8@uNɿdl[^?wi^!8|Oe;@b&&m??K 6>@AN@a a>B9B@9B@l30@0~5I@m\a޿h?X/@j@JTfI@hD>:8E/m>'@=?@DcQ@c_Ģ==?P<@ON%DQ@vow.h?8@4tVX^ J@c% RI=dI,3ؐK@@=.4?@xECmUyH-&@W9@ҊT0%@KI:)?FD5@aK@3M?69H@8I{3@T6K@->ǿ6ǿX=4HT`@sE@lc~~@^@ !<@^oM@k-C _v?w?,>+OG@OG@S,6C@ &QS h 7!?&@2(D@Z$P Ow&@%R`@xVef]H@T/b o=@o=@4r,`@ceH@!6;G&>¶U@c H@1T>B@ U@ }H@Q"W.O"@V8W@U.I@7.&0(H@2(H@+qW@:9=I@G$ȿh >)*H@w&@༽ޡ@$Z-U^Q?\@0v'@&W>?0B[](@(%4g\fYf1`?+e4@S:XKE@E&䑱v8-/@!./@vq 4@c*E@9'Hp}7? f2L@A?8@mDir?@xi!L@,"ur9@CW@́H?嚈@lc @)\גHHfg67^3@X9vnF@=;H"F?+@g3@ #nF@*`ti>l>0!N?@,gxJ@]?OF@OF@ʔ~&@@+J@d+hDܽ:?,7?@ J@_{- @hlj>?@^J@,N t$@Կ ǿ.=w?O@ TzT`@ '2E@^.?fe?O@{T`@t2E@/V{ʿK?L? |=@Wҿmҿ%? 3=@Wc?30:wH@R#Tnj!#@1CR.w?@wocE@ӏ%@? >$@8YE@w&@47Aemmn-@Ro-@rD@a<,@5u @$ @X4D@)Wx*@6Lh@J,E@@ҩ+)@)spkj%@P4MeE@@ 7f)@8R쿭H<@a\0j0+@O^'?05c(n0?큃M*@G%9s`1^.@fVu t/@kHz ?|n(;V H/@:_cŽ ?1UU*@/m>@8DU()@h;m.;$a@a@@[)3@ {F@[0m] 4>nDp2@kVF@T<VM "F?+@?$Gpg3@BT)nF@iVI?D@@007c3@ڿ$FF@>*)#/>jkeC@x@@ 9b?CC@@@J?1ܽ: k?{PF@kHG@TuY=@$2N?p@C@GHH@&WA=@^Abr_3 _D>U`@PbF@ǎЕB@B{c?T?SvA@LV=@I￁^'?lm_zA@*G[=@CrZҍ?:3P4@U+~D@zDv[??HD@e4@Ӄ ~D@Fw|3!{?XA@)&RA@m2'"M?Д~"A@@@GYX8>-?#H5@%jOL@HR0i0I? ?xk.@P ur4@PX~"|~?B1.@0LN5@!IBBK5>?>C@G@JP%ҿ)>->4xh C@ДAsG@M8D8D?ϡ?LqC@H^G@KG??6A@uII@LO?b3:A@x]>x>@NOg.Ś.}?H >@IL,Nu>@ԇ-I@‚>8@9ϛ?hޱI@ <]8@QU>W ?̉}m0|D (@6S*7#$>k+ݳ-@pJ6@0Q~AlI)?_t\,@(@T:<$]?w(@Эg~fXmIi ?_@@@0&@0m^ fYp).s2@t2@؃m3G@b*D@q nC nx:@pQ2F@N4[@E@jZu0j\\>a@eo2@qUF@v3W3A%`3@evY>G@\:A=K@iK@@wXH@xO.G@B]Lw]b>U5@U5@ԕH@'>3G@e^hDܽIN=)B@&6?@ZF J@aQL0S@Sz=@:7+@iF@r-HO>Sn+@R7 F@bjxxg?F@{\B@f'<&,'485R4@@H$J@g˾&@&@Adb(?@?%l0!Pa ?n8@ 9ԧq>@ s hX7,ȿ?I@_'T! ;@YaAzH?6H?P\0=@h+?jD&̊_=Hv]@@*^A@A!?#4@-w@@@ #}A@`k) ڽz>B9B@Hν$ @P|hBI@jH: 9(w>֌-@pi@I@l:a6ÿ @ t#@\[}K@0e @m !@`.;{K@s?&XY?l,@(3cI@nsu}f=*@/jE@YˋD@`/N0>= @|uWteF@oCC@oV=D @I<@ u=bG@4%^C@gPC?\)?؃m@G@` g2bC@r8Z8/a:DF@ Į"[E@rq([LXL D@6E@C:H2D?#@=C@E@ANg ?@P:z|F@ZHŸD@t"sM?30@mG@ss4 J@4AH0@!z@G@{ l>D@^84@8OPG@zw3[>?n[PE_@^M" h0L%?=_@7!ydEGĿ"?'A@е@@zLaL4?T?Q= `iA@| U0=@7br_?DA@E 4}?@F|&ɿk?+1@r\A@|!Tn2@@l8\!3@G&A@QZ@@}VG tܾ ?K-@8sN4s2@bp=E@gc7 ?_2@;E2,_3@X SD@[~l\03@~}XE@=nhɒ>xN?LA4@&1hE@ ?@gI@68@h՟6?.0@6q0@iLH@krB?06q0@bH@~ 4+?U, @@ʥۻ:~a^?[H@JFcAj@@E:}1R4,*=*=%8cB@e6g0'S/?x"@ ƒm+B@ (A@zh翟Ţ>G@Fd8?|D%@{Sx 7?K?ƚ\<@prX['@=濮Ǽa%>@= __?@y &@bC?T1@L&q=@o:F@yy<~Ⱦ>?1);<@DF@xZ& ek&@n8@-Gq>@mA J~M0ɾ\?'9@Ͼl1 wDȽ/@H+KC9@"wߍ17<=@B R@")(B@v?H@M\R@ {<µB@q lE@fé$@:_rG@u𒭿9>E7@ig>0#@"o5G@Jtc?Q*@@l:@CH@IaDwA?Dʾ@p]H@o-ҿ >'@'@)\!5@p&zkD@r=/uN?0@07qg&4@mC@y{>y ?/l)6@v0bCD@q>Fr΂5?\"ja6@A&xC(E@p/>.>?-j5@VcE@n x臿\?| L@ʂ2:@4#B侶2#?H@(t{I;@(WL@MlX?3:A@iV4@Z8Oke*'u>tJk>@,P' dj*aLKB>T?!!A@@'8?@O>\ ? ?@(U@Ր @X54@54@8 ,?@@a^%#"%?@J0Zw>@ @UVI G s?1/@(\2@33333SE@R 'E?&@h(D@@cZ$Nce>\ @ ?@ZrX`@SF)E@yp?=@FzVTQ)3@[KRc?89@vTQ HV 2@Com?7@³&Plc$@I87;=P{u$, @ `lib9@D&4J V̏@Dſ=:3Vq2@G2/Oʿ?LH@:(IV(Xg1@Z@|@+?@c);@nV!E@E}9?[=@[mގ<@fE@D( #@-}4C@0:1@@i@I@4hnB@6@sZ|=?^yH@^!m<&@uA>n]bT?U@'B.hBװ&@<e5/? "@nLOXC@ۧ1;>@zH@Ld@8˜%:;>!R*@Qܪc@T{?9x߿g)@(E2ߒLpg@j@$ߩ>n8@&f'6[@`h5@8;0K*?jVp_@C@8.5&뎃W@,}fI@5*Du< BQyq+]@YeH@.64:yByB`xZ@o3I@)R\O\@@J?8Y@KT(I@R3 G!F!- @œ9.ՎX@n:96I@%2;%??xP@J@CCD@"x?:@9T@,E@h0229S@>K@gMs=pWs=Nȵ?R>[R@@|J@j/7??Y>xqb Q@5|K@m񍨾5@xr\5DK@`b[I@9.,L,S~k?~;G@cs2H@B޴3M_ο+o?%!@f@% ,98*駿h?(96@H&@+_yǿLL?k,@+p7@@O3@,*k^?"80@cϽT 6s @,ۿeۿuE?nmB@"u0@BE'࿍5F?NI? /6B@J,@T|'O?d@tWPX-@&o&*?x4@FVx,~x+@A*M]a@@E@(> lT #R-@5#C+=?>@[yN@&W9@ ]>?J@H)(vB@e"g%+@4H~:@rlһK@_ @X;{7@:J@q*n=?7@ I@mC&ȿќ>@h@u'3@7([=E @KY4'@TBx7@dC;M>@ @^$yB@W-S?z|G@L%[xA@!>@ lW(;-@ >4#@`DG=,@xPTJ@i7lulS?=F@%gܞ@x}J@_k.U6=d?P:D@rv0bD@ ]:C @:.@N3I@}81+@Tw?)e@@L+v]@ˉz>D/f,E@BNvlB@oD ? I@0Zی:@K@H@HqU#M?oE@fPX0@b<KɿiV? Q3@jJ8XY*/ۼę!@1Dn=@E`6Z=?5F>@ /`'c@@/7@% ozѿ}>F@h"Ó:@Ќ{|iD@Z2n? @laW@44@/??4_mX@Y#@tH>+'@(k9ؗQhY2!^X>@H@3$Q-T&J\C.%s?|']T~b%= 3\s迊Q>2?"P8@ROL@i(`%2>?\@E@<#(5%@F5%@1%Ja`q8M@ WÿO6?O?c'Q`HfK@!G@(\µ^H@ 7م=q@- PSPBF@] K<8 @ 1*9@|г?@^ɿ?9@6@+艿j?p@B&@HMmM@:?ew?z(1@J?/_@<B@ailp@0 `@!]NC@;>1@8tcj 77@(21I󾿖>?dAw'@ HM1Pp7F@KToP\rKQ+ɿ a>@&3(QπRtQJW =QDjS?7LP貋e6ٿ<ʹ?]6Mi@ok#Y=bNNHr;NMh~ P'H辤p= ;Q@PH(as@@ڨ<,b@f:reD)zտ8?Ta@*w*3h?8e BV@XB6@+A>? ףp=SQk:@-F翄'@@Sףp= :@.࿕>]?Ԭ PK̤?9l,&>K?I_L,~_7>/o v6?ȡ@B%ԯJ3Z@ A E( >;?1!2Ilc@0<.[=@~:xOh:;5G@1δ<0@sNb4H@2 :>7@3_mw/H@3 /^? (LpWI@4rB=7@qtb`ģvK@5" H@ۧ1S_vOO@6 ׿jH@: xTNGsO@7izxxU%%HoP@83Fj>jRP@93(W7%@#ݛsWfFQ@;fE-Dx&@`!<8nM@=s:F? \i>"TR@>&GgDq= ףPZ)\hR@?XS5@(\ShdFR@@[+N{8?eU|JR@A+ >(\bYףp= WR@B"_%>P2*LW:Sh1R@Cx=?= ףp^Q@D=r!W@`/d7gWZmFR@E9?oF@wgX+0du%S@FxA@_Vv[S@Kf&C@[z\{S@L1@dm?^#W줃S@MQt%@.YmȔS@N6g;M8@_vOtUoŏS@O;/G@ QWT@R!?Y1@zi^[@p22@S2֚֚@Ԅe_@doH^I@`UFi f)?O@@iPu@tDNG띾!d?*XpMW@VLB?@E@Q1yQW1@ $|Cl ?@P2{ ?KWٹ8|bؿI?>C8Q(͆u'@-Ik{ @xvVSP4cLW!@X2kp?Vh@rJkxS+K$@]JZz'?7SA8ކC?YqkC@HYTW7@Zn?ib)@PΧO.(@{ffK@α>%(@BR˧uÿ#^]$fY(??`XnhMB@lCADOv?A[gnS@{>p+YE4@NFQ@d>A@C7@ qP@eQo@XVf@*1f >Lt?ew3Wf@)1gř;8c>NIh?@3u#@h\ۄE@p}~ۿU쾕@`C_@[hF@4޿ۤ.@0&*@B#@pDgi? hGQM@+?20?[ @;~*`4c\!@vi D17@؜c=A@wC4>aG#0T@" ; f&@__zM@] @z<K@ʿҡ1^@XJy#{>9N @]@JN |?900}̍ӹ^@z?0 }QT?z&'[@7V~a l?j0`@n݋69?yq8@_@?"S@+@}vZ`@˩&'wfȿ=@ ?a@ (: %?f@EIPa@@Clp\<"~ ^h*9D_@?#J?}Di? =@/dY`@DK"?LS8¹߾ @|1>r<@Bsx]@TDڌ@{6G?uTtZ@hVz̿t>?N +W@u> 6@>v鯿>n?@@oTX@7LB<@y7*>T%@CV@ 2ߐ;@@W!J?;+@*GT@hj.>@8xW:"T9@*<˘uS@L;A@4x\ KI?3@He'N Q@Ԟ$7@u#TF@xs-HV-P@tv"?*O] R.@{ָB@sֿo@`7#@KJ@D@raپܕ@@3b9S,^5f2@qX6@n`@A@pe(?#>a@x~}/B@o0 ?86@}0b@5E@(nK?l0@l0@أN6M@4 EI@kVXB#?#?PG1}N@J@li C C>> mKP@9>VW-K@@;,x6EE?q= ףA@@/fFPnI?Kά@@ffffffd%'A O?_ @I2D@;O = ?/@s~Z@ Ug,@c)!q?lV?@pNZ@j,@ ǎw?a*@4oY@__(@ b^J'>a>@n̕eZ@'A$@$\Wwҿ 0@9YB&@ @@[sۿ\@.mrT@Л@U^4@yH@x(Ē =? oG8-H]hD@@S 8=:=5@RaXףp= 9@PRF>VF>6@6@0CLY@HÝ I@ BpTL?@b }Y@a(@(O'ž@A 5 Y@@^̏@G"%@Ld@4{5F':?~?3u+@!,@Drؿ?ϯ@c @AQrmR'@?qy=!?@(a_8@"lxzvS@>d7@&Nw@2@xS@=o5YB@@Yr9@wT@<ˆO@|馠e@,uD;8wa?-(e@7mB :S@|h}>4c5RJ@1n83@ M2/=@1J@7I0@6HP vI?_oN@<]I=@1\2?>D¶_@04cT @0bfJ?ZB^@xOI$@/h!'k?y K]@0"@.͠F󾦔 x^@X'@-< @)`_@Hj LS(@,z.}@a^@#R#*@+3?K-@|^@ؚ 72@*,> @ 3sc@7 )t?r?KIsb@ `i&֣4?@?ɵL-yc@qC%w.H@gPbkǃ2@"?}za@^_I@!9ڿ1@bX9e$@M-P@ 4ɿf@jBvQ@Fq#j>Sr3Vf"[='Q@1ӑ<8@pWsa@#~j^R@j3hB@.4ib@1=aR@`?@U"b@^I R@}?#/@|L@eQ@Dp?uDZ@ ^S@B$>>$nI@F%q#T@)hEæ? %X@?o*RaS@Wi=D>@ d@MX#^@cvi5d@xN03#mvEU @i?9c@H 9= ‹([? I@p"\sD@3~Y@u w0?da/WlY@Ӗ1j4@ :)= ףpNHz%@>?rc "@ gN>@Wu?d=K^@Ԟu6@w >Xqc^3@1?ʒ3 c8!ӎL@穿A@A픵d @%N@&g?.PyelO@m7oԍ>DkY@}xv6@ ɠ$@dZard@+*-]M˾_~<@~<@8]D_@KJ@3?HF<L۵C@LV@e;@5;裾h?6Q@pW"E@Q¾],?7@¶P@D)B@4tCצQ-?=i9kR@-2C@EC>RUݞQ@d^GD@YB|F`<@<Ne'Gg}^{^ܹ(LC@MH@pC,?X.?aKW D@DYH@N.9??JC#v]@#II@O/ƿc/ƿ.@ڧSs.^@BJ@?<;пC??-@|k3t'@|zVu`@xU:6@uF@F⎼Z@dXC@`TR'-@d(ǿ??@8́tP@@@(˶"@`D8/Hb?*VVQ'',@'d[M\?03 N@|5A@oLH-RX?OAH)6@ )K@BϿ󮼫G@hˤf+Y, ,)@[U8쾰?cjk=@رbd1  xK=@p\9g@Y['\p@ (`n)@D524vor@3<-<@KA L@aj]E=@MY+@X0AG@VAW@&@))@063a@@NCv"ifPPѾi@#1+@.+GpH@]-+t?>>=73@+j0 E@pt?v0@`6@ThvH@  HQآ@ԧg2@ tH@u@B?2@Q͖x?@<1ɗ9sR?*=@\01?@Fb@6|Yq%(~?@`:oG@n0˿V4Z>t:@D@xJ8bS?r@ 5KY@0A?o5@<*,_@d%A@/$@ce˔>y'@07@`Ђ6M=!@@U?& @› ELAqd?Smÿ{mÿ$2@׻?@` Y9K@RWg@g@Ù_`ܘL@E.w?H2@AarH#@M2#羹?p B6ѽ$@924ݿ\;GC@W Np@#萿=O$@uF]Nw@ qW?? 4V@xL;@G|    O     3XAf !"#$j%& '(!$) Y*+",-#$./%&%-01 R23 w45'6(78)9:*+ :9*,P ;<-u =. >?/0 @A1 BC2 DE3N )F4 s G)4 HI56 J&  KL7$7 ,-8#l -K8$ MN98 NO:8 OP:7@ MN:9u QR; ST< UV= WX>09 Y??/^ ?Z?0 [\@A ]@ ^@_@'`aALbcBCqc:B,:bB+deDf'E%ghFGZijHjkIlmJmlJHnoK%CpqLhRrMhsNGtuN(ghNFvwOAwxOPfyzQR{HQ68|Q}7S ~T?XU>dXU0VVWCX2(C@XM@AX1Y_ZZB[2[HKB[\%yR%]^_` _3aba~cTcc,caQdYdUd0dHefJegD`g`gAgAhS9h+9h,qiLjklm<.lndop. p0qHur\rst uk9vnv_w+xy-x0bcxC<z <z-1+*{"f+{W|>YZ|?ZW|0o}K/{}6dvx~O*{* = b  G GF4 !+K!p!!Q!!|4"hY">Y/"/".&l"/&#\B#w##nK#F#[$v$$ %%A%y%!n5i66^6r7L$78r8+A9/%9[:: %; z> >2'?2' A'B'CW(D3W D3WEW7FWF(AGp-GGH% JLP7UJP:J:/K<KS=L^@M^NOiHmPL"Q]\L@wQaLSpL-qSQMSgNG+T.TXEUXH VUVNV  WXX MHY]Y _BZ _Zc[f[e&\( \ _]qi]lz_l`[oA`[o@aqHamdsmXerlerQg`hwijmn{po|Upssx ~Pt!"~t#v~$%- !&~c\bֆY|F `'E(*)rϊ|Q|*+,n-3.//m0021122<334455K66778899:;; <<n==>?h@ABBCCDDEE1FFGGkHHII%JJKKow!L ~Y wPMMNNOOPPBQQ7RRSSTTUU[VVWWXXJYYZZ4[[\\>]]^^H__]+,,+``\a1 1abcKdePfgUhi:jjkkDl~iSmn op- ,q8b q,$ "L "r s tt{ uuvvwwxotonn.}ySC}#$ zzcwc{[||}}U!~!"D##~$%h&&b')|,Q-.+1:2 %3 3:o6$#7I889X:;r=w>W>!?WV??W??W*@_@6@%9AA BHB'( CbCs GC%  D%$ D$ E {Ff EFf eGH I KN^TV4'V4W'bWXDlYDE3YEVZmZs[)*5\*]]N$^GY^tN_=.S`=`&aV%bc,ddfiL`i;5kl ltn9oogJphHr s!t"Wu#u$v%6w&Ky'z(e{){)}*~+mp,Npm,C-.-/01234;567Z8ߊ9T:{Nq;8;;]sr<r&<׏&s< = ==>? @ @A4@?Bɗ?.B..@BCș#"D~M"DRD#DE1FGk~fHI %I IoIJK.KL#KA=LrL Tl-MM N˷NNNNOPWĻQtQPnRPRW(R>-SsTMW>TUSqUQUGV`WW*o  b+CcX%6%l".%nga   Qf'X5Y5Y'JZZWu([ u[G>0,0yG(]6\M8]8KO87:V:8M:]S=<Uta\AL]L'E!9EnkH^lHmH}jkHIrMQRM;N.AuNGyQ%HIQ5IQ6UQ8Q)7S)$SQyyS(AX\U=m<Y_Zb<_q_%WH_`a%k`k__kYdk goNegh08(!mHXWjd,jHajlaobdoD%pNqbqdtq0qyqmamlrr%)rxuu_ wyKcx,xw   Z    =|dC|Yk=xu w' y\{z}Qz}Ro}%+g` _"! h?~  \8 \\Q6]bbc_b@,hq>0% Q`Rn%J-M  d!c#~X0d 0de 7epldefHlip_:y6(6pIcSM]     A 8&  \[ ('  bu    s N4  Xi    { < t("V=;-hgJ {q;$P== >>tt>W@ ^eH~HH  A2T<w!c~Lf~Lfk^[M NMeN NpO P iQR. Rtc Sm Sl!SrR">TG# U\# Ua$U(&%`j%`p&g &gZ'g'bH)hY)ha))*%*B*_*|*** * + + :+ g+++++,%,R,o,,,,, -=-Z-w--- -!-".#E.$b.%.&.'.(.)/*0/+M/,j/-/.///0/102H03e040506070819C1:`1;}1<1=1>1?2@.2AK2Bx2C2D2E2F 3G)3HF3Is3J3K3L3M4N$4OQ4P~4Q4R4S4T5U/5V\5Wy5X5Y5Z5[ 6\:6]W6^t6_6`6a6b6c7d"7e?7f\7gy7h7i7j7k7l 8m'8nT8oq8p8q8r8s8t9u9v<9wY9xv9y9z9{9|9}:~$:A:n::::;?;l;;;;<-<J<w<<<<<=5=R=o=====>>:>W>>>>>?%?B?_?|????? @*@G@d@@@@@@A/ALAiAAAAAAB4BQBnBBBBBBC9CVCsCCCCCD!D>D[DxDDDDD E&ECEpEEEEEFFKFhFFFFFF&GCG`G}GGGGGH+HHHeHHHHHHI0IMIjIIIIII(JUJJJJJ K #K @K ]K zKKKKL+LHLeLLLLLM#M@M]MzMMM M!M" N#(N$EN%bN&N'N(N)N*N+O,-O-JO.gO/O0O1O2O3O4P52P6OP7lP8P9P:P;P<P=Q>7Q?TQ@qQAQBQCQDQERFRG{|=>C-?wy@ARB HP+%<TC!wspeLeDPEFG  IJ HI xJKT <L[$MUNm#OpPuQR]:.(/012SqTjtUVWXYjAZ i[l\s]n`b^v_^q_rz` ax bcd efkg ~0}hi1j|lk{zlym xw~n ovHpu}|qtsrr{sqyozpt>|Eunmvlkwjhirsxglmfky e;zd{j|cb}a`~_^\][gZYegfXWaXVUT\]SPZ[ONMXYLKJIHI+89`H !G2TSF @D,B>Q;:7 Lt vut\ipqaonPW".V TVURSMLKJkFGDEABC?@~}|{ zwx=y>utsrqponmlkjihgf;<dec bwxa:_67\]^[ZY345XVW U RQ P) O N1LMKHIJGFED0BC?>=<;:.9 845673-2,10/.-, +!*")#('$&%%&$#'"(!) *+,-./012345*67 ) ( 8 %&'9 :$;<=>?@AB"#CDEFG!HIJK4LMNO PQ'R6STU;V@^bcdWAFXMYcZf h[u~\y]^QP_ON`a bcgGkj_^la} }Fm 8OuUt(nX$WzB'N@* d , 09 S: :X]/xWQ !"#$%&'()+,-0123456789=>?@ABCDEFGHIJKLMNORYZ[yefghopvwz{|}~ &mgdal-grass-2.0.0/autotest/ogr/ogr_grass.py000077500000000000000000000056301510331751700205460ustar00rootroot00000000000000#!/usr/bin/env pytest ############################################################################### # # Project: GDAL/OGR Test Suite # Purpose: GRASS Testing. # Author: Even Rouault # ############################################################################### # Copyright (c) 2009, Even Rouault # # SPDX-License-Identifier: MIT # ############################################################################### from osgeo import ogr import ogrtest ############################################################################### # Read 'polygon' datasource def test_ogr_load_driver(): import os print(os.environ['GDAL_DRIVER_PATH']) ogr_grassdriver = ogr.GetDriverByName("OGR_GRASS") assert ogr_grassdriver is not None, "Failed to load OGR_GRASS driver" def test_ogr_grass_polygon1(): ds = ogr.Open("./data/PERMANENT/vector/country_boundaries/head") assert ds is not None, "Cannot open datasource" assert ds.GetLayerCount() == 3, "wrong number of layers" lyr = ds.GetLayerByName("country_boundaries") assert lyr is not None, "Cannot find layer" wkt = "POLYGON ((22.9523771501665 41.3379938828111,22.8813737321974 41.9992971868503,22.3805257504246 42.3202595078151,22.5450118344096 42.461362006188,22.4365946794613 42.5803211533239,22.6048014665713 42.8985187851611,22.9860185075885 43.211161200527,22.5001566911803 43.642814439461,22.4104464047216 44.0080634629,22.657149692483 44.2349230006613,22.9448323910518 43.8237853053471,23.3323022803763 43.8970108099047,24.1006791521242 43.7410513372479,25.5692716814269 43.6884447291747,26.0651587256997 43.9434937607513,27.2423995297409 44.1759860296324,27.9701070492751 43.8124681666752,28.558081495892 43.7074616562581,28.0390950863847 43.2931716985742,27.673897739378 42.5778923610062,27.9967204119054 42.0073587102878,27.1357393734905 42.1414848903013,26.1170418637208 41.8269046087246,26.1061381365072 41.3288988307278,25.1972013689254 41.2344859889305,24.492644891058 41.583896185872,23.6920736019923 41.3090809189439,22.9523771501665 41.3379938828111))" feat = lyr.GetNextFeature() ogrtest.check_feature_geometry(feat, wkt) assert feat.GetFieldAsString("name") == "Bulgaria" assert feat.GetFieldAsDouble("POP_EST") == 7204687.0 def test_ogr_grass_polygon2(): ds = ogr.Open("./data/PERMANENT/vector/country_boundaries/head") lyr = ds.GetLayerByName("country_boundaries") feat = lyr.GetFeature(165) assert feat.GetFieldAsString("name") == "Luxembourg" wkt = """ POLYGON (( 5.67405195478483 49.5294835475575, 5.78241743330091 50.0903278672212, 6.04307335778111 50.1280516627942, 6.24275109215699 49.9022256536787, 6.18632042809418 49.4638028021145, 5.89775923017638 49.4426671413072, 5.67405195478483 49.5294835475575))""" ogrtest.check_feature_geometry(feat, wkt) gdal-grass-2.0.0/autotest/pymod/000077500000000000000000000000001510331751700165405ustar00rootroot00000000000000gdal-grass-2.0.0/autotest/pymod/gdaltest.py000066400000000000000000002022121510331751700207200ustar00rootroot00000000000000# -*- coding: utf-8 -*- ############################################################################### # # Project: GDAL/OGR Test Suite # Purpose: Python Library supporting GDAL/OGR Test Suite # Author: Frank Warmerdam # ############################################################################### # Copyright (c) 2003, Frank Warmerdam # Copyright (c) 2008-2013, Even Rouault # # SPDX-License-Identifier: MIT # ############################################################################### import contextlib import io import json import math import os import os.path import shlex import socket import stat import subprocess import sys import time import urllib.error import urllib.parse import urllib.request from queue import Queue from threading import Thread import pytest from osgeo import gdal, ogr, osr jp2kak_drv = None jpeg2000_drv = None jp2ecw_drv = None jp2mrsid_drv = None jp2openjpeg_drv = None jp2lura_drv = None jp2kak_drv_unregistered = False jpeg2000_drv_unregistered = False jp2ecw_drv_unregistered = False jp2mrsid_drv_unregistered = False jp2openjpeg_drv_unregistered = False jp2lura_drv_unregistered = False ############################################################################### def clean_tmp(): all_files = os.listdir("tmp") for filename in all_files: if filename in ["CVS", "do-not-remove"]: continue try: os.remove("tmp/" + filename) except OSError: pass ############################################################################### def testCreateCopyInterruptCallback(pct, message, user_data): # pylint: disable=unused-argument return pct <= 0.5 ############################################################################### class GDALTest(object): def __init__( self, drivername, filename, band, chksum, xoff=0, yoff=0, xsize=0, ysize=0, options=None, filename_absolute=0, chksum_after_reopening=None, open_options=None, ): self.driver = None self.drivername = drivername self.filename = filename if isinstance(self.filename, os.PathLike): self.filename = str(self.filename) self.filename_absolute = filename_absolute self.band = band self.chksum = chksum if chksum_after_reopening is not None: if isinstance(chksum_after_reopening, list): self.chksum_after_reopening = chksum_after_reopening else: self.chksum_after_reopening = [chksum_after_reopening] elif chksum is None: self.chksum_after_reopening = None else: self.chksum_after_reopening = [chksum] self.xoff = xoff self.yoff = yoff self.xsize = xsize self.ysize = ysize self.options = [] if options is None else options self.open_options = open_options def testDriver(self): if self.driver is None: self.driver = gdal.GetDriverByName(self.drivername) if self.driver is None: pytest.skip(self.drivername + " driver not found!") def testOpen( self, check_prj=None, check_gt=None, gt_epsilon=None, check_stat=None, check_approx_stat=None, stat_epsilon=None, skip_checksum=None, check_min=None, check_max=None, check_filelist=True, ): """check_prj - projection reference, check_gt - geotransformation matrix (tuple), gt_epsilon - geotransformation tolerance, check_stat - band statistics (tuple), stat_epsilon - statistics tolerance.""" self.testDriver() if self.filename_absolute: wrk_filename = self.filename else: wrk_filename = "data/" + self.filename if self.open_options: ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) assert ds is not None, "Failed to open dataset: " + wrk_filename assert ( ds.GetDriver().ShortName == gdal.GetDriverByName(self.drivername).ShortName ), "The driver of the returned dataset is %s instead of %s." % ( ds.GetDriver().ShortName, self.drivername, ) if self.xsize == 0 and self.ysize == 0: self.xsize = ds.RasterXSize self.ysize = ds.RasterYSize if ( check_filelist and ds.GetDriver().GetMetadataItem("DCAP_VIRTUALIO") is not None ): fl = ds.GetFileList() if fl is not None and fl and wrk_filename == fl[0]: # Copy all files in /vsimem/ mainfile_dirname = os.path.dirname(fl[0]) for filename in fl: target_filename = ( "/vsimem/tmp_testOpen/" + filename[len(mainfile_dirname) + 1 :] ) if stat.S_ISDIR(gdal.VSIStatL(filename).mode): gdal.Mkdir(target_filename, 0) else: f = gdal.VSIFOpenL(filename, "rb") assert f is not None, "File %s does not exist" % filename gdal.VSIFSeekL(f, 0, 2) size = gdal.VSIFTellL(f) gdal.VSIFSeekL(f, 0, 0) data = gdal.VSIFReadL(1, size, f) gdal.VSIFCloseL(f) if data is None: data = "" gdal.FileFromMemBuffer(target_filename, data) # Try to open the in-memory file main_virtual_filename = "/vsimem/tmp_testOpen/" + os.path.basename( fl[0] ) virtual_ds = gdal.Open(main_virtual_filename) virtual_ds_is_None = virtual_ds is None virtual_ds = None # Make sure the driver is specific enough by trying to open # with all other drivers but it drivers = [] for i in range(gdal.GetDriverCount()): drv_name = gdal.GetDriver(i).ShortName if drv_name.lower() != self.drivername.lower() and not ( ( drv_name.lower() == "gif" and self.drivername.lower() == "biggif" ) or ( drv_name.lower() == "biggif" and self.drivername.lower() == "gif" ) ): drivers += [drv_name] other_ds = None with gdal.ExceptionMgr(useExceptions=False): other_ds = gdal.OpenEx( main_virtual_filename, gdal.OF_RASTER, allowed_drivers=drivers ) other_ds_is_None = other_ds is None other_ds_driver_name = None if not other_ds_is_None: other_ds_driver_name = other_ds.GetDriver().ShortName other_ds = None for filename in gdal.ReadDirRecursive("/vsimem/tmp_testOpen"): gdal.Unlink("/vsimem/tmp_testOpen/" + filename) assert ( not virtual_ds_is_None ), "File list is not complete or driver does not support /vsimem/" assert ( other_ds_is_None ), "When excluding %s, dataset is still opened by driver %s" % ( self.drivername, other_ds_driver_name, ) # Do we need to check projection? if check_prj is not None: new_prj = ds.GetProjection() src_osr = osr.SpatialReference() src_osr.SetFromUserInput(check_prj) new_osr = osr.SpatialReference(wkt=new_prj) if not src_osr.IsSame(new_osr): print("") print("old = %s" % src_osr.ExportToPrettyWkt()) print("new = %s" % new_osr.ExportToPrettyWkt()) pytest.fail("Projections differ") # Do we need to check geotransform? if check_gt: # Default to 100th of pixel as our test value. if gt_epsilon is None: gt_epsilon = (abs(check_gt[1]) + abs(check_gt[2])) / 100.0 new_gt = ds.GetGeoTransform() for i in range(6): if new_gt[i] != pytest.approx(check_gt[i], abs=gt_epsilon): print("") print("old = ", check_gt) print("new = ", new_gt) pytest.fail("Geotransform differs.") oBand = ds.GetRasterBand(self.band) if skip_checksum is None: chksum = oBand.Checksum(self.xoff, self.yoff, self.xsize, self.ysize) # Do we need to check approximate statistics? if check_approx_stat: # Default to 1000th of pixel value range as our test value. if stat_epsilon is None: stat_epsilon = abs(check_approx_stat[1] - check_approx_stat[0]) / 1000.0 new_stat = oBand.GetStatistics(1, 1) for i in range(4): # NOTE - mloskot: Poor man Nan/Inf value check. It's poor # because we need to support old and buggy Python 2.3. # Tested on Linux, Mac OS X and Windows, with Python 2.3/2.4/2.5. sv = str(new_stat[i]).lower() assert not ("n" in sv or "i" in sv or "#" in sv), ( "NaN or infinity value encountered '%s'." % sv ) if new_stat[i] != pytest.approx(check_approx_stat[i], abs=stat_epsilon): print("") print("old = ", check_approx_stat) print("new = ", new_stat) pytest.fail("Approximate statistics differs.") # Do we need to check statistics? if check_stat: # Default to 1000th of pixel value range as our test value. if stat_epsilon is None: stat_epsilon = abs(check_stat[1] - check_stat[0]) / 1000.0 # FIXME: how to test approximate statistic results? new_stat = oBand.GetStatistics(1, 1) new_stat = oBand.GetStatistics(0, 1) for i in range(4): sv = str(new_stat[i]).lower() assert not ("n" in sv or "i" in sv or "#" in sv), ( "NaN or infinity value encountered '%s'." % sv ) if new_stat[i] != pytest.approx(check_stat[i], abs=stat_epsilon): print("") print("old = ", check_stat) print("new = ", new_stat) pytest.fail("Statistics differs.") if check_min: assert oBand.GetMinimum() == check_min, "Unexpected minimum value %s" % str( oBand.GetMinimum() ) if check_max: assert oBand.GetMaximum() == check_max, "Unexpected maximum value %s" % str( oBand.GetMaximum() ) ds = None assert not is_file_open(wrk_filename), "file still open after dataset closing" if skip_checksum is not None: return if self.chksum is None or chksum == self.chksum: return pytest.fail( 'Checksum for band %d in "%s" is %d, but expected %d.' % (self.band, self.filename, chksum, self.chksum) ) def testCreateCopy( self, check_minmax=1, check_gt=0, check_srs=None, vsimem=0, new_filename=None, strict_in=0, skip_preclose_test=0, delete_copy=1, gt_epsilon=None, check_checksum_not_null=None, interrupt_during_copy=False, dest_open_options=None, quiet_error_handler=True, ): self.testDriver() if self.filename_absolute: wrk_filename = self.filename else: wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) if self.band > 0: minmax = src_ds.GetRasterBand(self.band).ComputeRasterMinMax() src_gt = src_ds.GetGeoTransform() if new_filename is None: if vsimem: new_filename = "/vsimem/" + os.path.basename(self.filename) + ".tst" else: new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" if quiet_error_handler: gdal.PushErrorHandler("CPLQuietErrorHandler") if interrupt_during_copy: new_ds = self.driver.CreateCopy( new_filename, src_ds, strict=strict_in, options=self.options, callback=testCreateCopyInterruptCallback, ) else: new_ds = self.driver.CreateCopy( new_filename, src_ds, strict=strict_in, options=self.options ) if quiet_error_handler: gdal.PopErrorHandler() if interrupt_during_copy: if new_ds is None: with error_handler(): self.driver.Delete(new_filename) return new_ds = None self.driver.Delete(new_filename) pytest.fail("CreateCopy() should have failed due to interruption") assert new_ds is not None, ( "Failed to create test file using CreateCopy method." + "\n" + gdal.GetLastErrorMsg() ) assert ( new_ds.GetDriver().ShortName == gdal.GetDriverByName(self.drivername).ShortName ), "The driver of the returned dataset is %s instead of %s." % ( new_ds.GetDriver().ShortName, self.drivername, ) if self.band > 0 and skip_preclose_test == 0: bnd = new_ds.GetRasterBand(self.band) if check_checksum_not_null is True: assert bnd.Checksum() != 0, "Got null checksum on still-open file." elif self.chksum is not None and bnd.Checksum() != self.chksum: pytest.fail( "Did not get expected checksum on still-open file.\n" " Got %d instead of %d." % (bnd.Checksum(), self.chksum) ) if check_minmax: got_minmax = bnd.ComputeRasterMinMax() assert got_minmax == minmax, ( "Did not get expected min/max values on still-open file.\n" " Got %g,%g instead of %g,%g." % (got_minmax[0], got_minmax[1], minmax[0], minmax[1]) ) bnd = None new_ds = None # hopefully it's closed now! if dest_open_options is not None: new_ds = gdal.OpenEx( new_filename, gdal.OF_RASTER, open_options=dest_open_options ) else: new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename if self.band > 0: bnd = new_ds.GetRasterBand(self.band) if check_checksum_not_null is True: assert bnd.Checksum() != 0, "Got null checksum on reopened file." elif ( self.chksum_after_reopening is not None and bnd.Checksum() not in self.chksum_after_reopening ): pytest.fail( "Did not get expected checksum on reopened file.\n" " Got %d instead of %s." % (bnd.Checksum(), str(self.chksum_after_reopening)) ) if check_minmax: got_minmax = bnd.ComputeRasterMinMax() assert got_minmax == minmax, ( "Did not get expected min/max values on reopened file.\n" " Got %g,%g instead of %g,%g." % (got_minmax[0], got_minmax[1], minmax[0], minmax[1]) ) # Do we need to check the geotransform? if check_gt: if gt_epsilon is None: eps = 0.00000001 else: eps = gt_epsilon new_gt = new_ds.GetGeoTransform() if ( new_gt[0] != pytest.approx(src_gt[0], abs=eps) or new_gt[1] != pytest.approx(src_gt[1], abs=eps) or new_gt[2] != pytest.approx(src_gt[2], abs=eps) or new_gt[3] != pytest.approx(src_gt[3], abs=eps) or new_gt[4] != pytest.approx(src_gt[4], abs=eps) or new_gt[5] != pytest.approx(src_gt[5], abs=eps) ): print("") print("old = ", src_gt) print("new = ", new_gt) pytest.fail("Geotransform differs.") # Do we need to check the SRS? if check_srs == True: src_srs = src_ds.GetSpatialRef() new_srs = new_ds.GetSpatialRef() if src_srs is None and new_srs is not None: pytest.fail("src_srs is None and new_srs is not None") elif src_srs is not None and new_srs is None: pytest.fail("src_srs is not None and new_srs is None") elif ( src_srs is not None and new_srs is not None and not src_srs.IsSame(new_srs) ): print("") print("old = %s" % src_srs.ExportToPrettyWkt()) print("new = %s" % new_srs.ExportToPrettyWkt()) pytest.fail("Projections differ") elif check_srs is not None: new_prj = new_ds.GetProjection() src_osr = osr.SpatialReference(wkt=src_ds.GetProjection()) new_osr = osr.SpatialReference(wkt=new_prj) if not src_osr.IsSame(new_osr): print("") print("old = %s" % src_osr.ExportToPrettyWkt()) print("new = %s" % new_osr.ExportToPrettyWkt()) pytest.fail("Projections differ") bnd = None new_ds = None src_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON" and delete_copy == 1: self.driver.Delete(new_filename) def testCreate( self, vsimem=0, new_filename=None, out_bands=1, check_minmax=1, dest_open_options=None, delete_output_file=True, ): self.testDriver() if self.filename_absolute: wrk_filename = self.filename else: wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize src_img = src_ds.GetRasterBand(self.band).ReadRaster(0, 0, xsize, ysize) minmax = src_ds.GetRasterBand(self.band).ComputeRasterMinMax() if new_filename is None: if vsimem: new_filename = "/vsimem/" + self.filename + ".tst" else: new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" new_ds = self.driver.Create( new_filename, xsize, ysize, out_bands, src_ds.GetRasterBand(self.band).DataType, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." src_ds = None try: for band in range(1, out_bands + 1): new_ds.GetRasterBand(band).WriteRaster(0, 0, xsize, ysize, src_img) except Exception: pytest.fail("Failed to write raster bands to test file.") for band in range(1, out_bands + 1): assert ( self.chksum is None or new_ds.GetRasterBand(band).Checksum() == self.chksum ), ( "Did not get expected checksum on still-open file.\n" " Got %d instead of %d." % (new_ds.GetRasterBand(band).Checksum(), self.chksum) ) computed_minmax = new_ds.GetRasterBand(band).ComputeRasterMinMax() if computed_minmax != minmax and check_minmax: print("expect: ", minmax) print("got: ", computed_minmax) pytest.fail("Did not get expected min/max values on still-open file.") new_ds = None if dest_open_options is not None: new_ds = gdal.OpenEx( new_filename, gdal.OF_RASTER, open_options=dest_open_options ) else: new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename for band in range(1, out_bands + 1): assert ( self.chksum is None or new_ds.GetRasterBand(band).Checksum() == self.chksum ), ( "Did not get expected checksum on reopened file." " Got %d instead of %d." % (new_ds.GetRasterBand(band).Checksum(), self.chksum) ) assert ( new_ds.GetRasterBand(band).ComputeRasterMinMax() == minmax or not check_minmax ), "Did not get expected min/max values on reopened file." assert new_ds.FlushCache() == gdal.CE_None new_ds = None if delete_output_file and gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def testSetGeoTransform(self): self.testDriver() wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" new_ds = self.driver.Create( new_filename, xsize, ysize, 1, src_ds.GetRasterBand(self.band).DataType, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." gt = (123.0, 1.18, 0.0, 456.0, 0.0, -1.18) assert ( new_ds.SetGeoTransform(gt) is gdal.CE_None ), "Failed to set geographic transformation." src_ds = None new_ds = None new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename eps = 0.00000001 new_gt = new_ds.GetGeoTransform() if ( new_gt[0] != pytest.approx(gt[0], abs=eps) or new_gt[1] != pytest.approx(gt[1], abs=eps) or new_gt[2] != pytest.approx(gt[2], abs=eps) or new_gt[3] != pytest.approx(gt[3], abs=eps) or new_gt[4] != pytest.approx(gt[4], abs=eps) or new_gt[5] != pytest.approx(gt[5], abs=eps) ): print("") print("old = ", gt) print("new = ", new_gt) pytest.fail("Did not get expected geotransform.") new_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def testSetProjection(self, prj=None, expected_prj=None): self.testDriver() wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" new_ds = self.driver.Create( new_filename, xsize, ysize, 1, src_ds.GetRasterBand(self.band).DataType, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." gt = (123.0, 1.18, 0.0, 456.0, 0.0, -1.18) if prj is None: # This is a challenging SRS since it has non-meter linear units. prj = 'PROJCS["NAD83 / Ohio South",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",40.03333333333333],PARAMETER["standard_parallel_2",38.73333333333333],PARAMETER["latitude_of_origin",38],PARAMETER["central_meridian",-82.5],PARAMETER["false_easting",1968500],PARAMETER["false_northing",0],UNIT["US survey foot",0.3048006096012192]]' src_osr = osr.SpatialReference() src_osr.ImportFromWkt(prj) new_ds.SetGeoTransform(gt) assert ( new_ds.SetProjection(prj) is gdal.CE_None ), "Failed to set geographic projection string." src_ds = None new_ds = None new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename expected_osr = osr.SpatialReference() if expected_prj is None: expected_osr = src_osr else: expected_osr.ImportFromWkt(expected_prj) new_osr = osr.SpatialReference() new_osr.ImportFromWkt(new_ds.GetProjection()) if not new_osr.IsSame(expected_osr): print("Got: ") print(new_osr.ExportToPrettyWkt()) print("Expected:") print(expected_osr.ExportToPrettyWkt()) pytest.fail("Did not get expected projection reference.") new_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def testSetMetadata(self): self.testDriver() wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" new_ds = self.driver.Create( new_filename, xsize, ysize, 1, src_ds.GetRasterBand(self.band).DataType, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." new_ds.SetMetadata({"TEST_KEY": "TestValue"}) # FIXME # if new_ds.SetMetadata( dict ) is not gdal.CE_None: # print new_ds.SetMetadata( dict ) # post_reason( 'Failed to set metadata item.' ) # return 'fail' src_ds = None new_ds = None new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename md_dict = new_ds.GetMetadata() assert "TEST_KEY" in md_dict, "Metadata item TEST_KEY does not exist." assert md_dict["TEST_KEY"] == "TestValue", "Did not get expected metadata item." new_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def testSetNoDataValue(self, delete=False): self.testDriver() wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" dt = src_ds.GetRasterBand(self.band).DataType new_ds = self.driver.Create( new_filename, xsize, ysize, 1, dt, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." if dt == gdal.GDT_Int8: nodata = -11 elif self.options is None or "PIXELTYPE=SIGNEDBYTE" not in self.options: nodata = 130 else: nodata = 11 assert ( new_ds.GetRasterBand(1).SetNoDataValue(nodata) is gdal.CE_None ), "Failed to set NoData value." src_ds = None new_ds = None if delete: mode = gdal.GA_Update else: mode = gdal.GA_ReadOnly new_ds = gdal.Open(new_filename, mode) assert new_ds is not None, "Failed to open dataset: " + new_filename assert ( nodata == new_ds.GetRasterBand(1).GetNoDataValue() ), "Did not get expected NoData value." if delete: assert ( new_ds.GetRasterBand(1).DeleteNoDataValue() == 0 ), "Did not manage to delete nodata value" new_ds = None if delete: new_ds = gdal.Open(new_filename) assert ( new_ds.GetRasterBand(1).GetNoDataValue() is None ), "Got nodata value whereas none was expected" new_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def testSetNoDataValueAndDelete(self): self.testSetNoDataValue(delete=True) def testSetDescription(self): self.testDriver() wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" new_ds = self.driver.Create( new_filename, xsize, ysize, 1, src_ds.GetRasterBand(self.band).DataType, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." description = "Description test string" new_ds.GetRasterBand(1).SetDescription(description) src_ds = None new_ds = None new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename assert ( description == new_ds.GetRasterBand(1).GetDescription() ), "Did not get expected description string." new_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def testSetUnitType(self): self.testDriver() wrk_filename = "data/" + self.filename if self.open_options: src_ds = gdal.OpenEx( wrk_filename, gdal.OF_RASTER, open_options=self.open_options ) else: src_ds = gdal.Open(wrk_filename, gdal.GA_ReadOnly) xsize = src_ds.RasterXSize ysize = src_ds.RasterYSize new_filename = "tmp/" + os.path.basename(self.filename) + ".tst" new_ds = self.driver.Create( new_filename, xsize, ysize, 1, src_ds.GetRasterBand(self.band).DataType, options=self.options, ) assert new_ds is not None, "Failed to create test file using Create method." unit = "mg/m3" assert ( new_ds.GetRasterBand(1).SetUnitType(unit) is gdal.CE_None ), "Failed to set unit type." src_ds = None new_ds = None new_ds = gdal.Open(new_filename) assert new_ds is not None, "Failed to open dataset: " + new_filename new_unit = new_ds.GetRasterBand(1).GetUnitType() if new_unit != unit: print("") print("old = ", unit) print("new = ", new_unit) pytest.fail("Did not get expected unit type.") new_ds = None if gdal.GetConfigOption("CPL_DEBUG", "OFF") != "ON": self.driver.Delete(new_filename) def approx_equal(a, b): a = float(a) b = float(b) if a == 0 and b != 0: return 0 if b / a != pytest.approx(1.0, abs=0.00000000001): return 0 return 1 def user_srs_to_wkt(user_text): srs = osr.SpatialReference() srs.SetFromUserInput(user_text) return srs.ExportToWkt() def equal_srs_from_wkt(expected_wkt, got_wkt, verbose=True): expected_srs = osr.SpatialReference() expected_srs.SetAxisMappingStrategy(osr.OAMS_AUTHORITY_COMPLIANT) expected_srs.ImportFromWkt(expected_wkt) got_srs = osr.SpatialReference() got_srs.SetAxisMappingStrategy(osr.OAMS_AUTHORITY_COMPLIANT) got_srs.ImportFromWkt(got_wkt) if got_srs.IsSame(expected_srs): return True if verbose: print("Expected:\n%s" % expected_wkt) print("Got: \n%s" % got_wkt) print("SRS differs from expected.") return False ############################################################################### # Compare two sets of RPC metadata, and establish if they are essentially # equivalent or not. def check_rpcs_equal(md1, md2): __tracebackhide__ = True simple_fields = [ "LINE_OFF", "SAMP_OFF", "LAT_OFF", "LONG_OFF", "HEIGHT_OFF", "LINE_SCALE", "SAMP_SCALE", "LAT_SCALE", "LONG_SCALE", "HEIGHT_SCALE", ] coef_fields = [ "LINE_NUM_COEFF", "LINE_DEN_COEFF", "SAMP_NUM_COEFF", "SAMP_DEN_COEFF", ] for sf in simple_fields: assert sf in md1 assert sf in md2 assert approx_equal(float(md1[sf]), float(md2[sf])) for cf in coef_fields: list1 = md1[cf].split() list2 = md2[cf].split() assert len(list1) == 20, "%s value list length wrong(1)" % cf assert len(list2) == 20, "%s value list length wrong(2)" % cf for i in range(20): assert approx_equal( float(list1[i]), float(list2[i]) ), "%s[%d] values differ." % (cf, i) ############################################################################### # Test if geotransforms are equal with an epsilon tolerance # def check_geotransform(gt1, gt2, gt_epsilon): __tracebackhide__ = True assert gt1 == pytest.approx(gt2, abs=gt_epsilon), "Geotransform differs." ############################################################################### # Download file at url 'url' and put it as 'filename' in 'tmp/cache/' # # If 'filename' already exits in 'tmp/cache/', it is not downloaded # If GDAL_DOWNLOAD_TEST_DATA is not defined, the function fails # If GDAL_DOWNLOAD_TEST_DATA is defined, 'url' is downloaded as 'filename' in 'tmp/cache/' def download_file( url, filename=None, download_size=-1, force_download=False, max_download_duration=None, base_dir="tmp/cache", ): if filename is None: filename = os.path.basename(url) elif filename.startswith(base_dir + "/"): filename = filename[len(base_dir + "/") :] if os.path.exists(os.path.join(base_dir, filename)): return True if not (force_download or download_test_data()): return False val = None start_time = time.time() try: handle = gdalurlopen(url) if handle is None: return False if download_size == -1: try: handle_info = handle.info() download_size = int(handle_info["content-length"]) print("Downloading %s (length = %d bytes)..." % (url, download_size)) except Exception: print("Downloading %s..." % (url)) else: print("Downloading %d bytes from %s..." % (download_size, url)) except Exception: return False if download_size >= 0: sys.stdout.write("Progress: ") nLastTick = -1 val = "".encode("ascii") while len(val) < download_size or download_size < 0: chunk_size = 1024 if download_size >= 0 and len(val) + chunk_size > download_size: chunk_size = download_size - len(val) try: chunk = handle.read(chunk_size) except Exception: print("Did not get expected data length.") return False val = val + chunk if len(chunk) < chunk_size: if download_size < 0: break print("Did not get expected data length.") return False if download_size >= 0: nThisTick = int(40 * len(val) / download_size) while nThisTick > nLastTick: nLastTick = nLastTick + 1 if nLastTick % 4 == 0: sys.stdout.write("%d" % int((nLastTick / 4) * 10)) else: sys.stdout.write(".") nLastTick = nThisTick if nThisTick == 40: sys.stdout.write(" - done.\n") current_time = time.time() if ( max_download_duration is not None and current_time - start_time > max_download_duration ): print("Download aborted due to timeout.") return False try: os.stat(base_dir) except OSError: os.mkdir(base_dir) try: open(base_dir + "/" + filename, "wb").write(val) return True except IOError: print("Cannot write %s" % (filename)) return False # Attempt to download file using `download_file`; skip test in case of failure def download_or_skip(url, *args, **kwargs): __tracebackhide__ = True msg = io.StringIO() with contextlib.redirect_stdout(msg): success = download_file(url, *args, **kwargs) if not success: if download_test_data(): pytest.skip(f"Failed to download {url} : {msg.getvalue()}") else: pytest.skip("GDAL_DOWNLOAD_TEST_DATA is not set to YES") ############################################################################### # GDAL data type to python struct format def gdal_data_type_to_python_struct_format(datatype): type_char = "B" if datatype == gdal.GDT_Int16: type_char = "h" elif datatype == gdal.GDT_UInt16: type_char = "H" elif datatype == gdal.GDT_Int32: type_char = "i" elif datatype == gdal.GDT_UInt32: type_char = "I" elif datatype == gdal.GDT_Float32: type_char = "f" elif datatype == gdal.GDT_Float64: type_char = "d" return type_char ############################################################################### # Compare the values of the pixels def compare_ds(ds1, ds2, xoff=0, yoff=0, width=0, height=0, verbose=1): import struct if width == 0: width = ds1.RasterXSize if height == 0: height = ds1.RasterYSize data1 = ds1.GetRasterBand(1).ReadRaster(xoff, yoff, width, height) type_char = gdal_data_type_to_python_struct_format(ds1.GetRasterBand(1).DataType) val_array1 = struct.unpack(type_char * width * height, data1) data2 = ds2.GetRasterBand(1).ReadRaster(xoff, yoff, width, height) type_char = gdal_data_type_to_python_struct_format(ds2.GetRasterBand(1).DataType) val_array2 = struct.unpack(type_char * width * height, data2) maxdiff = 0.0 ndiffs = 0 for i in range(width * height): diff = val_array1[i] - val_array2[i] if diff != 0: # print(val_array1[i]) # print(val_array2[i]) ndiffs = ndiffs + 1 if abs(diff) > maxdiff: maxdiff = abs(diff) if verbose: print( "Diff at pixel (%d, %d) : %f" % (i % width, i / width, float(diff)) ) elif ndiffs < 10: if verbose: print( "Diff at pixel (%d, %d) : %f" % (i % width, i / width, float(diff)) ) if maxdiff != 0 and verbose: print("Max diff : %d" % (maxdiff)) print("Number of diffs : %d" % (ndiffs)) return maxdiff ############################################################################### # Deregister all JPEG2000 drivers, except the one passed as an argument def deregister_all_jpeg2000_drivers_but(name_of_driver_to_keep): global jp2kak_drv, jpeg2000_drv, jp2ecw_drv, jp2mrsid_drv, jp2openjpeg_drv, jp2lura_drv global jp2kak_drv_unregistered, jpeg2000_drv_unregistered, jp2ecw_drv_unregistered, jp2mrsid_drv_unregistered, jp2openjpeg_drv_unregistered, jp2lura_drv_unregistered # Deregister other potential conflicting JPEG2000 drivers that will # be re-registered in the cleanup jp2kak_drv = gdal.GetDriverByName("JP2KAK") if name_of_driver_to_keep != "JP2KAK" and jp2kak_drv: gdal.Debug("gdaltest", "Deregistering JP2KAK") jp2kak_drv.Deregister() jp2kak_drv_unregistered = True jpeg2000_drv = gdal.GetDriverByName("JPEG2000") if name_of_driver_to_keep != "JPEG2000" and jpeg2000_drv: gdal.Debug("gdaltest", "Deregistering JPEG2000") jpeg2000_drv.Deregister() jpeg2000_drv_unregistered = True jp2ecw_drv = gdal.GetDriverByName("JP2ECW") if name_of_driver_to_keep != "JP2ECW" and jp2ecw_drv: gdal.Debug("gdaltest.", "Deregistering JP2ECW") jp2ecw_drv.Deregister() jp2ecw_drv_unregistered = True jp2mrsid_drv = gdal.GetDriverByName("JP2MrSID") if name_of_driver_to_keep != "JP2MrSID" and jp2mrsid_drv: gdal.Debug("gdaltest.", "Deregistering JP2MrSID") jp2mrsid_drv.Deregister() jp2mrsid_drv_unregistered = True jp2openjpeg_drv = gdal.GetDriverByName("JP2OpenJPEG") if name_of_driver_to_keep != "JP2OpenJPEG" and jp2openjpeg_drv: gdal.Debug("gdaltest.", "Deregistering JP2OpenJPEG") jp2openjpeg_drv.Deregister() jp2openjpeg_drv_unregistered = True jp2lura_drv = gdal.GetDriverByName("JP2Lura") if name_of_driver_to_keep != "JP2Lura" and jp2lura_drv: gdal.Debug("gdaltest.", "Deregistering JP2Lura") jp2lura_drv.Deregister() jp2lura_drv_unregistered = True return True ############################################################################### # Re-register all JPEG2000 drivers previously disabled by # deregister_all_jpeg2000_drivers_but def reregister_all_jpeg2000_drivers(): global jp2kak_drv, jpeg2000_drv, jp2ecw_drv, jp2mrsid_drv, jp2openjpeg_drv, jp2lura_drv global jp2kak_drv_unregistered, jpeg2000_drv_unregistered, jp2ecw_drv_unregistered, jp2mrsid_drv_unregistered, jp2openjpeg_drv_unregistered, jp2lura_drv_unregistered if jp2kak_drv_unregistered: jp2kak_drv.Register() jp2kak_drv_unregistered = False gdal.Debug("gdaltest", "Registering JP2KAK") if jpeg2000_drv_unregistered: jpeg2000_drv.Register() jpeg2000_drv_unregistered = False gdal.Debug("gdaltest", "Registering JPEG2000") if jp2ecw_drv_unregistered: jp2ecw_drv.Register() jp2ecw_drv_unregistered = False gdal.Debug("gdaltest", "Registering JP2ECW") if jp2mrsid_drv_unregistered: jp2mrsid_drv.Register() jp2mrsid_drv_unregistered = False gdal.Debug("gdaltest", "Registering JP2MrSID") if jp2openjpeg_drv_unregistered: jp2openjpeg_drv.Register() jp2openjpeg_drv_unregistered = False gdal.Debug("gdaltest", "Registering JP2OpenJPEG") if jp2lura_drv_unregistered: jp2lura_drv.Register() jp2lura_drv_unregistered = False gdal.Debug("gdaltest", "Registering JP2Lura") return True ############################################################################### # Determine if the filesystem supports sparse files. # Currently, this will only work on Linux (or any *NIX that has the stat # command line utility) def filesystem_supports_sparse_files(path): if gdal.GetConfigOption("TRAVIS", None): return False try: (ret, err) = runexternal_out_and_err(f'stat -f -c "%T" {path}') except OSError: return False if err != "": print("Cannot determine if filesystem supports sparse files") return False if "fat32" in ret: print("File system does not support sparse files") return False if "wslfs" in ret or "0x53464846" in ret: # wslfs for older stat versions print( "Windows Subsystem for Linux FS is at the time of " + "writing not known to support sparse files" ) return False # Add here any missing filesystem supporting sparse files # See http://en.wikipedia.org/wiki/Comparison_of_file_systems filesystems_supporting_sparse_files = { "ext3", "ext4", "reiser", "xfs", "jfs", "zfs", "ntfs", } for fs in filesystems_supporting_sparse_files: if fs in ret: return True print("Filesystem %s is not believed to support sparse files" % ret) return False ############################################################################### # Unzip a file def unzip(target_dir, zipfilename, verbose=False): try: import zipfile zf = zipfile.ZipFile(zipfilename) except ImportError: os.system("unzip -d " + target_dir + " " + zipfilename) return for filename in zf.namelist(): if verbose: print(filename) outfilename = os.path.join(target_dir, filename) if filename.endswith("/"): if not os.path.exists(outfilename): os.makedirs(outfilename) else: outdirname = os.path.dirname(outfilename) if not os.path.exists(outdirname): os.makedirs(outdirname) outfile = open(outfilename, "wb") outfile.write(zf.read(filename)) outfile.close() return isnan = math.isnan ############################################################################### # Return NaN def NaN(): return float("nan") ############################################################################### # Return positive infinity def posinf(): return float("inf") ############################################################################### # Return negative infinity def neginf(): return float("-inf") ############################################################################### # Has the user requested to download test data def download_test_data(): val = gdal.GetConfigOption("GDAL_DOWNLOAD_TEST_DATA", None) return val == "yes" or val == "YES" ############################################################################### # Has the user requested to run the slow tests def run_slow_tests(): val = gdal.GetConfigOption("GDAL_RUN_SLOW_TESTS", None) return val == "yes" or val == "YES" ############################################################################### # Return true if the platform support symlinks def support_symlink(): if sys.platform.startswith("linux"): return True if sys.platform.find("freebsd") != -1: return True if sys.platform == "darwin": return True if sys.platform.find("sunos") != -1: return True return False ############################################################################### # Return True if the test must be skipped def skip_on_travis(): __tracebackhide__ = True val = gdal.GetConfigOption("TRAVIS", None) if val is not None: pytest.skip("Test skipped on Travis") ############################################################################### # Return True if the provided name is in TRAVIS_BRANCH or BUILD_NAME def is_travis_branch(name): if "TRAVIS_BRANCH" in os.environ: val = os.environ["TRAVIS_BRANCH"] if name in val: return True if "BUILD_NAME" in os.environ: val = os.environ["BUILD_NAME"] if name in val: return True return False ############################################################################### # Return True if we run under CI def is_ci(): return "CI" in os.environ ############################################################################### # find_lib_linux() # Parse /proc/self/maps to find an occurrence of libXXXXX.so.* def find_lib_linux(libname): f = open("/proc/self/maps") lines = f.readlines() f.close() for line in lines: if line.rfind("/lib" + libname) == -1 or line.find(".so") == -1: continue i = line.find(" ") if i < 0: continue line = line[i + 1 :] i = line.find(" ") if i < 0: continue line = line[i + 1 :] i = line.find(" ") if i < 0: continue line = line[i + 1 :] i = line.find(" ") if i < 0: continue line = line[i + 1 :] i = line.find(" ") if i < 0: continue line = line[i + 1 :] soname = line.lstrip().rstrip("\n") if soname.rfind("/lib" + libname) == -1: continue return soname return None ############################################################################### # find_lib_sunos() # Parse output of pmap to find an occurrence of libXXX.so.* def find_lib_sunos(libname): pid = os.getpid() lines, _ = runexternal_out_and_err("pmap %d" % pid) for line in lines.split("\n"): if line.rfind("/lib" + libname) == -1 or line.find(".so") == -1: continue i = line.find("/") if i < 0: continue line = line[i:] soname = line.lstrip().rstrip("\n") if soname.rfind("/lib" + libname) == -1: continue return soname return None ############################################################################### # find_lib_windows() # use Module32First() / Module32Next() API on the current process def find_lib_windows(libname): try: import ctypes except ImportError: return None kernel32 = ctypes.windll.kernel32 MAX_MODULE_NAME32 = 255 MAX_PATH = 260 TH32CS_SNAPMODULE = 0x00000008 class MODULEENTRY32(ctypes.Structure): _fields_ = [ ("dwSize", ctypes.c_int), ("th32ModuleID", ctypes.c_int), ("th32ProcessID", ctypes.c_int), ("GlblcntUsage", ctypes.c_int), ("ProccntUsage", ctypes.c_int), ("modBaseAddr", ctypes.c_char_p), ("modBaseSize", ctypes.c_int), ("hModule", ctypes.c_void_p), ("szModule", ctypes.c_char * (MAX_MODULE_NAME32 + 1)), ("szExePath", ctypes.c_char * MAX_PATH), ] Module32First = kernel32.Module32First Module32First.argtypes = [ctypes.c_void_p, ctypes.POINTER(MODULEENTRY32)] Module32First.rettypes = ctypes.c_int Module32Next = kernel32.Module32Next Module32Next.argtypes = [ctypes.c_void_p, ctypes.POINTER(MODULEENTRY32)] Module32Next.rettypes = ctypes.c_int CreateToolhelp32Snapshot = kernel32.CreateToolhelp32Snapshot CreateToolhelp32Snapshot.argtypes = [ctypes.c_int, ctypes.c_int] CreateToolhelp32Snapshot.rettypes = ctypes.c_void_p CloseHandle = kernel32.CloseHandle CloseHandle.argtypes = [ctypes.c_void_p] CloseHandle.rettypes = ctypes.c_int GetLastError = kernel32.GetLastError GetLastError.argtypes = [] GetLastError.rettypes = ctypes.c_int snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0) if snapshot is None: return None soname = None i = 0 while True: entry = MODULEENTRY32() entry.dwSize = ctypes.sizeof(MODULEENTRY32) pentry = ctypes.pointer(entry) if i == 0: ret = Module32First(snapshot, pentry) else: ret = Module32Next(snapshot, pentry) i = i + 1 if ret == 0: break try: path = entry.szExePath.decode("latin1") except Exception: continue i = path.rfind("\\" + libname) if i < 0: continue if path[i + 1 :].find("\\") >= 0: continue # Avoid matching gdal_PLUGIN.dll if path[i + 1 :].find("_") >= 0: continue soname = path break CloseHandle(snapshot) return soname ############################################################################### # find_lib() def find_lib(mylib): if sys.platform.startswith("linux"): return find_lib_linux(mylib) if sys.platform.startswith("sunos"): return find_lib_sunos(mylib) if sys.platform.startswith("win32"): return find_lib_windows(mylib) # sorry mac users or other BSDs # should be doable return None ############################################################################### # get_opened_files() get_opened_files_has_warned = False def get_opened_files(): if not sys.platform.startswith("linux"): return [] fdpath = "/proc/%d/fd" % os.getpid() if not os.path.exists(fdpath): global get_opened_files_has_warned if not get_opened_files_has_warned: get_opened_files_has_warned = True print("get_opened_files() not supported due to /proc not being readable") return [] file_numbers = os.listdir(fdpath) filenames = [] for fd in file_numbers: try: filename = os.readlink("%s/%s" % (fdpath, fd)) if ( not filename.startswith("/dev/") and not filename.startswith("pipe:") and filename.find("proj.db") < 0 ): filenames.append(filename) except OSError: pass return filenames ############################################################################### # is_file_open() def is_file_open(filename): for got_filename in get_opened_files(): if filename in got_filename: return True return False ############################################################################### # built_against_curl() def built_against_curl(): return "CURL_ENABLED=YES" in gdal.VersionInfo("BUILD_INFO") ############################################################################### # error_handler() # Allow use of "with" for an ErrorHandler that always pops at the scope close. # Defaults to suppressing errors and warnings. @contextlib.contextmanager def error_handler(error_name="CPLQuietErrorHandler"): handler = gdal.PushErrorHandler(error_name) try: yield handler finally: gdal.PopErrorHandler() ############################################################################### # Temporarily define a new value of block cache @contextlib.contextmanager def SetCacheMax(val): oldval = gdal.GetCacheMax() gdal.SetCacheMax(val) try: yield finally: gdal.SetCacheMax(oldval) ############################################################################### # Temporarily enable exceptions for gdal, ogr and osr modules @contextlib.contextmanager def enable_exceptions(): from osgeo import ogr, osr try: with gdal.ExceptionMgr(useExceptions=True), osr.ExceptionMgr( useExceptions=True ), ogr.ExceptionMgr(useExceptions=True): yield except AttributeError: # GDAL <3.7 yield ############################################################################### # Temporarily disable exceptions for gdal, ogr and osr modules @contextlib.contextmanager def disable_exceptions(): from osgeo import ogr, osr try: with gdal.ExceptionMgr(useExceptions=False), osr.ExceptionMgr( useExceptions=False ), ogr.ExceptionMgr(useExceptions=False): yield except AttributeError: # GDAL <3.7 yield ############################################################################### # Temporarily define a configuration option try: config_option = gdal.config_option except AttributeError: # GDAL <3.7 @contextlib.contextmanager def config_option(key, val): oldval = gdal.GetConfigOption(key) gdal.SetConfigOption(key, val) try: yield finally: gdal.SetConfigOption(key, oldval) ############################################################################### # Temporarily define a set of configuration options try: config_options = gdal.config_options except AttributeError: # GDAL <3.7 @contextlib.contextmanager def config_options(options): oldvals = {key: gdal.GetConfigOption(key) for key in options} for key in options: gdal.SetConfigOption(key, options[key]) try: yield finally: for key in options: gdal.SetConfigOption(key, oldvals[key]) ############################################################################### # Temporarily define VSI credentials credential_keys = set() @contextlib.contextmanager def credentials(prefix, options): global credential_keys # Special processing for nested with credentials() call on the same key clear_credentials = prefix not in credential_keys credential_keys.add(prefix) for key in options: gdal.SetCredential(prefix, key, options[key]) try: yield finally: if clear_credentials: credential_keys.remove(prefix) gdal.ClearCredentials(prefix) ############################################################################### # Temporarily create a file @contextlib.contextmanager def tempfile(filename, content): gdal.FileFromMemBuffer(filename, content) try: yield finally: gdal.Unlink(filename) ############################################################################### def gdalurlopen(url, timeout=10): old_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(timeout) proxy = None if "GDAL_HTTP_PROXY" in os.environ: proxy = os.environ["GDAL_HTTP_PROXY"] protocol = "http" if "GDAL_HTTPS_PROXY" in os.environ and url.startswith("https"): proxy = os.environ["GDAL_HTTPS_PROXY"] protocol = "https" if proxy is not None: if "GDAL_HTTP_PROXYUSERPWD" in os.environ: proxyuserpwd = os.environ["GDAL_HTTP_PROXYUSERPWD"] proxy_handler = urllib.request.ProxyHandler( {protocol: f"{protocol}://{proxyuserpwd}@{proxy}"} ) else: proxyuserpwd = None proxy_handler = urllib.request.ProxyHandler( {protocol: f"{protocol}://{proxy}"} ) opener = urllib.request.build_opener(proxy_handler, urllib.request.HTTPHandler) urllib.request.install_opener(opener) try: handle = urllib.request.urlopen(url) socket.setdefaulttimeout(old_timeout) return handle except urllib.error.HTTPError as e: print(f"HTTP service for {url} is down (HTTP Error: {e.code})") socket.setdefaulttimeout(old_timeout) return None except urllib.error.ContentTooShortError: print(f"HTTP content too short for {url}.") socket.setdefaulttimeout(old_timeout) return None except urllib.error.URLError as e: print(f"HTTP service for {url} is down (URL Error: {e.reason})") socket.setdefaulttimeout(old_timeout) return None except socket.timeout: print(f"HTTP service for {url} timed out") socket.setdefaulttimeout(old_timeout) return None def runexternal( cmd, strin=None, check_memleak=True, display_live_on_parent_stdout=False, encoding="latin1", ): # pylint: disable=unused-argument if sys.platform == "win32": command = cmd else: command = shlex.split(cmd) if strin is None: p = subprocess.Popen(command, stdout=subprocess.PIPE) else: p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.stdin.write(strin.encode("ascii")) p.stdin.close() if p.stdout is not None: if display_live_on_parent_stdout: ret = "" ret_stdout = p.stdout while True: c = ret_stdout.read(1).decode(encoding) if c == "": break ret = ret + c sys.stdout.write(c) else: ret = p.stdout.read().decode(encoding) else: ret = "" waitcode = p.wait() if waitcode != 0: ret = f"{ret}\nERROR ret code = {waitcode}" return ret def _read_in_thread(f, q): q.put(f.read()) f.close() def runexternal_out_and_err(cmd, check_memleak=True, encoding="ascii"): # pylint: disable=unused-argument if sys.platform == "win32": command = cmd else: command = shlex.split(cmd) p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if p.stdout is not None: q_stdout = Queue() t_stdout = Thread(target=_read_in_thread, args=(p.stdout, q_stdout)) t_stdout.start() else: q_stdout = None ret_stdout = "" if p.stderr is not None: q_stderr = Queue() t_stderr = Thread(target=_read_in_thread, args=(p.stderr, q_stderr)) t_stderr.start() else: q_stderr = None ret_stderr = "" if q_stdout is not None: ret_stdout = q_stdout.get().decode(encoding) if q_stderr is not None: ret_stderr = q_stderr.get().decode(encoding) waitcode = p.wait() if waitcode != 0: ret_stderr = f"{ret_stderr}\nERROR ret code = {waitcode}" return (ret_stdout, ret_stderr) ############################################################################### # Validate JSON according to a JSON schema # # "jsn" should be a string or a json object # "schema" should be a path to file containing a JSON schema. If the # file is not found relative to the current working directory or GDAL_DATA, # the test will fail. def validate_json(jsn, schema): __tracebackhide__ = True try: import jsonschema except ImportError: pytest.skip("jsonschema module not available") if not os.path.exists(schema): gdal_data = gdal.GetConfigOption("GDAL_DATA") if gdal_data and os.path.exists(os.path.join(gdal_data, schema)): schema = os.path.join(gdal_data, schema) else: pytest.fail(f"Could not find schema {schema}") if isinstance(jsn, str): jsn = json.loads(jsn) schema = json.loads(open(schema, "rb").read()) if sys.version_info >= (3, 8): from importlib.metadata import version jsonschema_version = version("jsonschema") else: from pkg_resources import get_distribution jsonschema_version = get_distribution("jsonschema").version def versiontuple(v): return tuple(map(int, (v.split(".")))) # jsonschema 4.18 deprecates automatic resolution of "$ref" for security # reason. Use a custom retrieve method. # Cf https://python-jsonschema.readthedocs.io/en/latest/referencing/#automatically-retrieving-resources-over-http if versiontuple(jsonschema_version) >= (4, 18): from referencing import Registry, Resource def retrieve_remote_file(uri: str): if not uri.startswith("http://") and not uri.startswith("https://"): raise Exception(f"Cannot retrieve {uri}") os.makedirs("tmp/cache", exist_ok=True) filename = "tmp/cache/" + os.path.basename(uri) if not download_file(uri, filename=filename, force_download=True): raise Exception(f"Cannot download {uri}") response = open(filename, "rb").read() return Resource.from_contents(json.loads(response)) registry = Registry(retrieve=retrieve_remote_file) validator_cls = jsonschema.validators.validator_for(schema) validator_cls(schema, registry=registry).validate(jsn) else: # jsonschema < 4.18 try: jsonschema.validate(instance=jsn, schema=schema) except jsonschema.exceptions.RefResolutionError: pytest.skip("Failed to resolve remote reference in JSON schema") ############################################################################### # Close and reopen a dataset def reopen(ds, update=False, open_options=None): ds_loc = ds.GetDescription() ds_drv = ds.GetDriver() ds.Close() if isinstance(ds, ogr.DataSource) and open_options is None: return ogr.Open(ds_loc, update) flags = 0 if update: flags = gdal.OF_UPDATE if open_options is None: open_options = {} return gdal.OpenEx( ds_loc, flags, allowed_drivers=[ds_drv.GetDescription()], open_options=open_options, ) gdal-grass-2.0.0/autotest/pymod/ogrtest.py000066400000000000000000000331031510331751700206010ustar00rootroot00000000000000############################################################################### # $Id$ # # Project: GDAL/OGR Test Suite # Purpose: Support functions for OGR tests. # Author: Frank Warmerdam # ############################################################################### # Copyright (c) 2003, Frank Warmerdam # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. ############################################################################### import contextlib import sys sys.path.append("../pymod") import gdaltest import pytest from osgeo import ogr geos_flag = None sfcgal_flag = None ############################################################################### def check_features_against_list(layer, field_name, value_list): __tracebackhide__ = True field_index = layer.GetLayerDefn().GetFieldIndex(field_name) assert field_index >= 0, f"did not find required field {field_name}" for i, value in enumerate(value_list): feat = layer.GetNextFeature() assert ( feat is not None ), f"Got only {i} features, not the expected {len(value_list)} features." failure_message = ( "field %s feature %d did not match expected value %s, got %s." % (field_name, i, str(value), str(feat.GetField(field_index))) ) if isinstance(value, type("str")): assert feat.GetFieldAsString(field_index) == value, failure_message else: assert feat.GetField(field_index) == value, failure_message feat = layer.GetNextFeature() assert feat is None, "got more features than expected" ############################################################################### @gdaltest.disable_exceptions() def check_feature_geometry( actual, expected, max_error=0.0001, *, pointwise=False, context=None, _root_actual=None, _root_expected=None, ): """ Check that a geometry matches an expected value. :param actual: ogr.Feature or ogr.Geometry to test :param expected: ogr.Geometry or WKT string representing expected value :param max_error: maximum error in any single ordinate (applies to pointwise comparison only) :param pointwise: if True, a pointwise comparison will be used to check structural equality (ring ordering, orientation, etc.) if False, the pointwise comparison will be done only if a topological equality check (ST_Equals semantics) fails. :param context: Optional context information to include in assertion failure message (e.g., "i = 5") """ __tracebackhide__ = True if type(actual) is ogr.Feature: actual = actual.GetGeometryRef() if context: context_msg = f" ({context})" else: context_msg = "" if expected is not None and isinstance(expected, type("a")): expected = ogr.CreateGeometryFromWkt(expected) assert expected is not None, ( "failed to parse expected geometry as WKT" + context_msg ) if expected is None: assert actual is None, "expected NULL geometry but got one" + context_msg return else: assert actual is not None, "expected geometry but got NULL" + context_msg assert expected.GetGeometryName() == expected.GetGeometryName() assert actual.GetGeometryName() == expected.GetGeometryName(), ( "geometry types do not match" + context_msg ) assert actual.GetGeometryCount() == expected.GetGeometryCount(), ( "sub-geometry counts do not match" + context_msg ) assert actual.GetPointCount() == expected.GetPointCount(), ( "point counts do not match" + context_msg ) if expected.Is3D(): assert actual.Is3D(), "expected Z dimension not found" if actual.Is3D(): assert expected.Is3D(), "unexpected Z dimension" if expected.IsMeasured(): assert actual.IsMeasured(), "expected M dimension not found" if actual.IsMeasured(): assert expected.IsMeasured(), "unexpected M dimension" # ST_Equals(a,b) <==> ST_Within(a,b) && ST_Within(b,a) # We can't use OGRGeometry::Equals() because it doesn't test spatial # equality, but structural one # Within does not take into account Z or M values, so we skip to the # pointwise check if they are present. if ( (not pointwise) and have_geos() and actual.Within(expected) and expected.Within(actual) and (not actual.Is3D()) and (not actual.IsMeasured()) ): return if _root_actual is None: _root_actual = actual if _root_expected is None: _root_expected = expected if actual.GetGeometryCount() > 0: count = actual.GetGeometryCount() for i in range(count): check_feature_geometry( actual.GetGeometryRef(i), expected.GetGeometryRef(i), max_error, context=context, _root_actual=_root_actual, _root_expected=_root_expected, ) else: count = actual.GetPointCount() if ogr.GT_Flatten(actual.GetGeometryType()) == ogr.wkbPoint: count = 1 for i in range(count): actual_pt = [actual.GetX(i), actual.GetY(i)] expected_pt = [expected.GetX(i), expected.GetY(i)] if actual.Is3D() or expected.Is3D(): actual_pt.append(actual.GetZ(i)) expected_pt.append(expected.GetZ(i)) if actual.IsMeasured() or expected.IsMeasured(): # Hack to deal with shapefile not-a-number M values that equal to -1.79769313486232e+308 if actual.GetM(i) >= -1.7e308 and expected.GetM(i) >= -1.7e308: actual_pt.append(actual.GetM(i)) expected_pt.append(expected.GetM(i)) assert actual_pt == pytest.approx( expected_pt, abs=max_error ), f"Error in vertex {i+1}/{count} exceeds tolerance. {context_msg}\n Expected: {_root_expected.ExportToWkt()}\n Actual: {_root_actual.ExportToWkt()}" ############################################################################### def check_feature(feat, feat_ref, max_error=0.0001, excluded_fields=None): __tracebackhide__ = True for i in range(feat.GetGeomFieldCount()): check_feature_geometry( feat.GetGeomFieldRef(i), feat_ref.GetGeomFieldRef(i), max_error=max_error ) for i in range(feat.GetFieldCount()): if excluded_fields is not None: if feat.GetDefnRef().GetFieldDefn(i).GetName() in excluded_fields: continue assert feat.GetField(i) == feat_ref.GetField( i ), f"Field {i}, expected {feat.GetField(i)}, got {feat_ref.GetField(i)}" ############################################################################### def compare_layers(lyr, lyr_ref, excluded_fields=None): for f_ref in lyr_ref: f = lyr.GetNextFeature() assert f is not None, "not enough features" check_feature(f, f_ref, excluded_fields=excluded_fields) f = lyr.GetNextFeature() assert f is None, "more features than expected" ############################################################################### def get_wkt_data_series(with_z, with_m, with_gc, with_circular, with_surface): basic_wkts = [ "POINT (1 1)", "POINT (1.1234 1.4321)", "POINT (1.12345678901234 1.4321)", "POINT (1.2 -2.1)", "MULTIPOINT ((10 40),(40 30),(20 20),(30 10))", "LINESTRING (1.2 -2.1,2.4 -4.8)", "MULTILINESTRING ((10 10,20 20,10 40),(40 40,30 30,40 20,30 10),(50 50,60 60,50 90))", "MULTILINESTRING ((1.2 -2.1,2.4 -4.8))", "POLYGON ((30 10,40 40,20 40,10 20,30 10))", "POLYGON ((35 10,45 45,15 40,10 20,35 10),(20 30,35 35,30 20,20 30))", "MULTIPOLYGON (((30 20,45 40,10 40,30 20)),((15 5,40 10,10 20,5 10,15 5)))", "MULTIPOLYGON (((40 40,20 45,45 30,40 40)),((20 35,10 30,10 10,30 5,45 20,20 35),(30 20,20 15,20 25,30 20)))", "MULTIPOLYGON (((30 20,45 40,10 40,30 20)))", "MULTIPOLYGON (((35 10,45 45,15 40,10 20,35 10),(20 30,35 35,30 20,20 30)))", ] gc_wkts = [ "GEOMETRYCOLLECTION (POINT (4 6),LINESTRING (4 6,7 10))", "GEOMETRYCOLLECTION (POINT (4 6),GEOMETRYCOLLECTION (POINT (4 6),LINESTRING (4 6,7 10)))", ] circular_wkts = [ "CIRCULARSTRING (0 0,1 1,1 0)", "COMPOUNDCURVE (CIRCULARSTRING (0 0,1 1,1 0),(1 0,0 1))", "CURVEPOLYGON (CIRCULARSTRING (0 0,4 0,4 4,0 4,0 0),(1 1,3 3,3 1,1 1))", "MULTICURVE ((0 0,5 5),CIRCULARSTRING (4 0,4 4,8 4))", "MULTISURFACE (CURVEPOLYGON (CIRCULARSTRING (0 0,4 0,4 4,0 4,0 0),(1 1,3 3,3 1,1 1)),((10 10,14 12,11 10,10 10),(11 11,11.5 11.0,11.0 11.5,11 11)))", ] surface_wkts = [ "POLYHEDRALSURFACE Z (((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 0,1 1 1,1 0 1,1 0 0,1 1 0)),((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)))", "TRIANGLE ((0 0,0 9,9 0,0 0))", "TIN Z (((0 0 0,0 0 1,0 1 0,0 0 0)))", "TIN Z (((0 0 0,0 0 1,0 1 0,0 0 0)),((0 0 0,0 1 0,1 1 0,0 0 0)))", ] wkts = basic_wkts wkts_with_z = [] wkts_with_m = [] wkts_with_zm = [] if with_gc: wkts.extend(gc_wkts) if with_circular: wkts.extend(circular_wkts) if with_z or with_m: for i, wkt in enumerate(wkts): if with_z: g = ogr.CreateGeometryFromWkt(wkt) g.Set3D(True) wkts_with_z.extend([g.ExportToIsoWkt()]) if with_z: g = ogr.CreateGeometryFromWkt(wkt) g.SetMeasured(True) wkts_with_m.extend([g.ExportToIsoWkt()]) if with_z and with_m: g = ogr.CreateGeometryFromWkt(wkt) g.Set3D(True) g.SetMeasured(True) wkts_with_zm.extend([g.ExportToIsoWkt()]) wkts.extend(wkts_with_z) wkts.extend(wkts_with_m) wkts.extend(wkts_with_zm) if with_surface: wkts.extend(surface_wkts) return wkts ############################################################################### def quick_create_layer_def(lyr, field_list): # Each field is a tuple of (name, type, width, precision) # Any of type, width and precision can be skipped. Default type is string. for field in field_list: name = field[0] if len(field) > 1: field_type = field[1] else: field_type = ogr.OFTString field_defn = ogr.FieldDefn(name, field_type) if len(field) > 2: field_defn.SetWidth(int(field[2])) if len(field) > 3: field_defn.SetPrecision(int(field[3])) lyr.CreateField(field_defn) ############################################################################### def quick_create_feature(layer, field_values, wkt_geometry): feature = ogr.Feature(feature_def=layer.GetLayerDefn()) for i, field_value in enumerate(field_values): feature.SetField(i, field_value) if wkt_geometry is not None: geom = ogr.CreateGeometryFromWkt(wkt_geometry) if geom is None: raise ValueError("Failed to create geometry from: " + wkt_geometry) feature.SetGeometryDirectly(geom) result = layer.CreateFeature(feature) if result != 0: raise ValueError("CreateFeature() failed in ogrtest.quick_create_feature()") ############################################################################### def have_geos(): return ogr.GetGEOSVersionMajor() > 0 ############################################################################### def have_sfcgal(): global sfcgal_flag if sfcgal_flag is None: with gdaltest.disable_exceptions(): pnt1 = ogr.CreateGeometryFromWkt("POINT(10 20 30)") pnt2 = ogr.CreateGeometryFromWkt("POINT(40 50 60)") sfcgal_flag = pnt1.Distance3D(pnt2) >= 0 return sfcgal_flag ############################################################################### # Temporarily set an attribute filter @contextlib.contextmanager def attribute_filter(lyr, filter_txt): lyr.SetAttributeFilter(filter_txt) try: yield finally: lyr.SetAttributeFilter(None) ############################################################################### # Temporarily set a spatial filter # Single argument is parsed as WKT or assumed to be an OGRGeometry # Four arguments are interpreted as bounding rectangle @contextlib.contextmanager def spatial_filter(lyr, *args): if len(args) == 1: if type(args[0]) is str: geom = ogr.CreateGeometryFromWkt(args[0]) lyr.SetSpatialFilter(geom) else: lyr.SetSpatialFilter(args[0]) elif len(args) == 4: lyr.SetSpatialFilterRect(*args) else: raise Exception("Unknown spatial filter type") try: yield finally: lyr.SetSpatialFilter(None) gdal-grass-2.0.0/autotest/pytest.ini.in000066400000000000000000000015341510331751700200510ustar00rootroot00000000000000; PYTEST_INI_HEADER_MESSAGE [pytest] python_files = *.py testpaths = ogr gdrivers norecursedirs = ogr/data gdrivers/data log_file = autotest.log log_file_level = INFO env = @TEST_ENV@ markers = random_order: Indicates whether tests can be run non-sequentially require_curl: Skip test(s) if curl support is absent require_creation_option: Skip test(s) if required creation option is not available require_driver: Skip test(s) if driver isn't present require_geos: Skip test(s) if required GEOS version is not available require_proj: Skip test(s) if required PROJ version is not available require_run_on_demand: Skip test(s) if RUN_ON_DEMAND environment variable is not set slow: Skip test(s) if GDAL_RUN_SLOW_TESTS environment variable is not set # Make sure that all markers are declared above addopts = --strict-markers gdal-grass-2.0.0/cmake/000077500000000000000000000000001510331751700146205ustar00rootroot00000000000000gdal-grass-2.0.0/cmake/FindGDAL.cmake000066400000000000000000000224511510331751700171360ustar00rootroot00000000000000# FindGDAL # -------- # Copyright (c) 2007, Magnus Homann Redistribution and # use is allowed according to the terms of the BSD license. For details see the # accompanying COPYING-CMAKE-SCRIPTS file. # # Once run this will define: # # GDAL_FOUND = system has GDAL lib # # GDAL_LIBRARY = full path to the library # # GDAL_INCLUDE_DIR = where to find headers include(${CMAKE_SOURCE_DIR}/cmake/MacPlistMacros.cmake) if(WIN32) if(MINGW) find_path(GDAL_INCLUDE_DIR gdal.h /usr/local/include /usr/include c:/msys/local/include PATH_SUFFIXES gdal) find_library( GDAL_LIBRARY NAMES gdal PATHS /usr/local/lib /usr/lib c:/msys/local/lib) endif(MINGW) if(MSVC) find_path(GDAL_INCLUDE_DIR gdal.h "$ENV{LIB_DIR}/include/gdal" $ENV{INCLUDE}) find_library( GDAL_LIBRARY NAMES gdal gdal_i PATHS "$ENV{LIB_DIR}/lib" $ENV{LIB} /usr/lib c:/msys/local/lib) if(GDAL_LIBRARY) set(GDAL_LIBRARY;odbc32;odbccp32 CACHE STRING INTERNAL) endif(GDAL_LIBRARY) endif(MSVC) elseif(APPLE AND QGIS_MAC_DEPS_DIR) find_path(GDAL_INCLUDE_DIR gdal.h "$ENV{LIB_DIR}/include") find_library( GDAL_LIBRARY NAMES gdal PATHS "$ENV{LIB_DIR}/lib") else(WIN32) if(UNIX) # try to use framework on mac want clean framework path, not unix # compatibility path if(APPLE) if(CMAKE_FIND_FRAMEWORK MATCHES "FIRST" OR CMAKE_FRAMEWORK_PATH MATCHES "ONLY" OR NOT CMAKE_FIND_FRAMEWORK) set(CMAKE_FIND_FRAMEWORK_save ${CMAKE_FIND_FRAMEWORK} CACHE STRING "" FORCE) set(CMAKE_FIND_FRAMEWORK "ONLY" CACHE STRING "" FORCE) find_library(GDAL_LIBRARY GDAL) if(GDAL_LIBRARY) # they're all the same in a framework set(GDAL_INCLUDE_DIR ${GDAL_LIBRARY}/Headers CACHE PATH "Path to a file.") # set GDAL_CONFIG to make later test happy, not used here, may not # exist set(GDAL_CONFIG ${GDAL_LIBRARY}/unix/bin/gdal-config CACHE FILEPATH "Path to a program.") # version in info.plist get_version_plist(${GDAL_LIBRARY}/Resources/Info.plist GDAL_VERSION) if(NOT GDAL_VERSION) message( FATAL_ERROR "Could not determine GDAL version from framework.") endif(NOT GDAL_VERSION) string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" GDAL_VERSION_MAJOR "${GDAL_VERSION}") string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" GDAL_VERSION_MINOR "${GDAL_VERSION}") if(GDAL_VERSION_MAJOR LESS 3) message( FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 3.2 or higher.") endif(GDAL_VERSION_MAJOR LESS 3) if((GDAL_VERSION_MAJOR EQUAL 3) AND (GDAL_VERSION_MINOR LESS 2)) message( FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 3.2 or higher.") endif((GDAL_VERSION_MAJOR EQUAL 3) AND (GDAL_VERSION_MINOR LESS 2)) endif(GDAL_LIBRARY) set(CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE) endif() endif(APPLE) if(CYGWIN) find_library( GDAL_LIBRARY NAMES gdal PATHS /usr/lib /usr/local/lib) endif(CYGWIN) if(NOT GDAL_INCLUDE_DIR OR NOT GDAL_LIBRARY OR NOT GDAL_CONFIG) # didn't find OS X framework, and was not set by user set(GDAL_CONFIG_PREFER_PATH "$ENV{GDAL_HOME}/bin" CACHE STRING "preferred path to GDAL (gdal-config)") set(GDAL_CONFIG_PREFER_FWTOOLS_PATH "$ENV{FWTOOLS_HOME}/bin_safe" CACHE STRING "preferred path to GDAL (gdal-config) from FWTools") find_program( GDAL_CONFIG gdal-config ${GDAL_CONFIG_PREFER_PATH} ${GDAL_CONFIG_PREFER_FWTOOLS_PATH} $ENV{LIB_DIR}/bin /usr/local/bin/ /usr/bin/) # MESSAGE("DBG GDAL_CONFIG ${GDAL_CONFIG}") if(GDAL_CONFIG) # extract gdal version execute_process( COMMAND ${GDAL_CONFIG} --version OUTPUT_VARIABLE GDAL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" GDAL_VERSION_MAJOR "${GDAL_VERSION}") string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" GDAL_VERSION_MINOR "${GDAL_VERSION}") string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\3" GDAL_VERSION_MICRO "${GDAL_VERSION}") # MESSAGE("DBG GDAL_VERSION ${GDAL_VERSION}") MESSAGE("DBG # GDAL_VERSION_MAJOR ${GDAL_VERSION_MAJOR}") MESSAGE("DBG # GDAL_VERSION_MINOR ${GDAL_VERSION_MINOR}") # check for gdal version version 1.2.5 is known NOT to be supported # (missing CPL_STDCALL macro) According to INSTALL, 2.1+ is required if(GDAL_VERSION_MAJOR LESS 3) message( FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 3.0 or higher.") endif(GDAL_VERSION_MAJOR LESS 3) # IF ( (GDAL_VERSION_MAJOR EQUAL 2) AND (GDAL_VERSION_MINOR LESS 1) ) # MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use # 2.1 or higher.") ENDIF( (GDAL_VERSION_MAJOR EQUAL 2) AND # (GDAL_VERSION_MINOR LESS 1) ) if((GDAL_VERSION_MAJOR EQUAL 3) AND (GDAL_VERSION_MINOR EQUAL 0) AND (GDAL_VERSION_MICRO LESS 3)) message( FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 3.0.3 or higher.") endif( (GDAL_VERSION_MAJOR EQUAL 3) AND (GDAL_VERSION_MINOR EQUAL 0) AND (GDAL_VERSION_MICRO LESS 3)) # set INCLUDE_DIR to prefix+include execute_process( COMMAND ${GDAL_CONFIG} --prefix OUTPUT_VARIABLE GDAL_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) # SET(GDAL_INCLUDE_DIR ${GDAL_PREFIX}/include CACHE STRING INTERNAL) find_path(GDAL_INCLUDE_DIR gdal.h ${GDAL_PREFIX}/include/gdal ${GDAL_PREFIX}/include /usr/local/include /usr/include) # extract link dirs for rpath execute_process( COMMAND ${GDAL_CONFIG} --libs OUTPUT_VARIABLE GDAL_CONFIG_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE) # split off the link dirs (for rpath) use regular expression to match # wildcard equivalent "-L*" with is a space or a # semicolon string(REGEX MATCHALL "[-][L]([^ ;])+" GDAL_LINK_DIRECTORIES_WITH_PREFIX "${GDAL_CONFIG_LIBS}") # MESSAGE("DBG # GDAL_LINK_DIRECTORIES_WITH_PREFIX=${GDAL_LINK_DIRECTORIES_WITH_PREFIX}") # remove prefix -L because we need the pure directory for # LINK_DIRECTORIES if(GDAL_LINK_DIRECTORIES_WITH_PREFIX) string(REGEX REPLACE "[-][L]" "" GDAL_LINK_DIRECTORIES ${GDAL_LINK_DIRECTORIES_WITH_PREFIX}) endif(GDAL_LINK_DIRECTORIES_WITH_PREFIX) # split off the name use regular expression to match wildcard equivalent # "-l*" with is a space or a semicolon string(REGEX MATCHALL "[-][l]([^ ;])+" GDAL_LIB_NAME_WITH_PREFIX "${GDAL_CONFIG_LIBS}") # MESSAGE("DBG GDAL_LIB_NAME_WITH_PREFIX=${GDAL_LIB_NAME_WITH_PREFIX}") # remove prefix -l because we need the pure name if(GDAL_LIB_NAME_WITH_PREFIX) string(REGEX REPLACE "[-][l]" "" GDAL_LIB_NAME ${GDAL_LIB_NAME_WITH_PREFIX}) endif(GDAL_LIB_NAME_WITH_PREFIX) if(APPLE) if(NOT GDAL_LIBRARY) # work around empty GDAL_LIBRARY left by framework check while still # preserving user setting if given ***FIXME*** need to improve # framework check so below not needed set(GDAL_LIBRARY ${GDAL_LINK_DIRECTORIES}/lib${GDAL_LIB_NAME}.dylib CACHE STRING INTERNAL FORCE) endif(NOT GDAL_LIBRARY) else(APPLE) find_library( GDAL_LIBRARY NAMES ${GDAL_LIB_NAME} gdal PATHS ${GDAL_LINK_DIRECTORIES}/lib ${GDAL_LINK_DIRECTORIES}) endif(APPLE) else(GDAL_CONFIG) message( "FindGDAL.cmake: gdal-config not found. Please set it manually. GDAL_CONFIG=${GDAL_CONFIG}" ) endif(GDAL_CONFIG) endif( NOT GDAL_INCLUDE_DIR OR NOT GDAL_LIBRARY OR NOT GDAL_CONFIG) endif(UNIX) endif(WIN32) if(GDAL_INCLUDE_DIR AND GDAL_LIBRARY) set(GDAL_FOUND TRUE) endif(GDAL_INCLUDE_DIR AND GDAL_LIBRARY) if(GDAL_FOUND) if(NOT GDAL_FIND_QUIETLY) file(READ ${GDAL_INCLUDE_DIR}/gdal_version.h gdal_version) string(REGEX REPLACE "^.*GDAL_RELEASE_NAME +\"([^\"]+)\".*$" "\\1" GDAL_RELEASE_NAME "${gdal_version}") message(STATUS "Found GDAL: ${GDAL_LIBRARY} (${GDAL_RELEASE_NAME})") endif(NOT GDAL_FIND_QUIETLY) else(GDAL_FOUND) message(GDAL_INCLUDE_DIR=${GDAL_INCLUDE_DIR}) message(GDAL_LIBRARY=${GDAL_LIBRARY}) message(FATAL_ERROR "Could not find GDAL") endif(GDAL_FOUND) gdal-grass-2.0.0/cmake/FindGRASS.cmake000066400000000000000000000050341510331751700173040ustar00rootroot00000000000000#[=======================================================================[.rst: FindGRASS --------- Find GRASS GIS. This module uses ``grass --config`` to extract necessary information from GRASS GIS. Result Variables ^^^^^^^^^^^^^^^^ This module will set the following variables in your project: ``GRAS_FOUND`` True if GRASS is found. ``GRASS_BIN`` Path to the grass binary. ``GRASS_GISBASE`` Path to the GRASS GISBASE directory. ``GRASS_VERSION`` The version of GRASS found. Cache variables ^^^^^^^^^^^^^^^ The following cache variables may also be set: ``GRASS_BIN`` Path to the grass binary. ``GRASS_BIN_PREFER_PATH`` Path to the directory containing the grass binary Hints ^^^^^ Set ``GRASS_BIN`` to the GRASS executable binary file or ``GRASS_BIN_PREFER_PATH`` to the path containing the same. E.g. ``-DGRASS_BIN=/usr/bin/grass`` or ``-DGRASS_BIN_PREFER_PATH=/usr/bin``. #]=======================================================================] set(ENV{LC_ALL} "en_US.UTF-8") set(GRASS_BIN_PREFER_PATH "" CACHE STRING "Preferred path to directory containing GRASS binary (grass[N])") if(NOT GRASS_BIN) foreach(grass_search_version "" 8 7) find_program( GRASS_BIN grass${grass_search_version} PATHS ${GRASS_BIN_PREFER_PATH} /usr/local/bin /usr/bin /sw/bin /opt/local/bin /opt/csw/bin /opt/bin DOC "Path to the grass[N] binary") endforeach() endif() if(GRASS_BIN) execute_process( COMMAND ${GRASS_BIN} --config path OUTPUT_VARIABLE GRASS_GISBASE OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process( COMMAND ${GRASS_BIN} --config version OUTPUT_VARIABLE GRASS_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GRASS REQUIRED_VARS GRASS_BIN GRASS_GISBASE GRASS_VERSION) if(GRASS_FOUND) message(STATUS "Found GRASS_GISBASE: ${GRASS_GISBASE} (${GRASS_VERSION})") else() message(FATAL_ERROR "Failed to find GRASS") endif() set(GRASS_INCLUDE "${GRASS_GISBASE}/include") mark_as_advanced(GRASS_INCLUDE) set(G_RASTLIBS -lgrass_raster -lgrass_imagery) set(G_VECTLIBS -lgrass_vector -lgrass_dig2 -lgrass_dgl -lgrass_rtree -lgrass_linkm -lgrass_dbmiclient -lgrass_dbmibase) set(G_LIBS -L${GRASS_GISBASE}/lib ${G_VECTLIBS} ${G_RASTLIBS} -lgrass_gproj -lgrass_gmath -lgrass_gis -lgrass_datetime -lgrass_btree2 -lgrass_ccmath) gdal-grass-2.0.0/cmake/FindPROJ.cmake000066400000000000000000000024701510331751700172000ustar00rootroot00000000000000#[=======================================================================[.rst: FindPROJ -------- Find PROJ's include path. Result variables ^^^^^^^^^^^^^^^^ This module will set the following variables in your project: ``PROJ_INCLUDE_DIRS`` the directories of the PROJ headers (proj.h etc.) ``PROJ_FOUND`` True if PROJ is found. Hints ^^^^^ Set ``PROJ_INCLUDE_DIR`` environment variable to specify the location of non-standard location of PROJ include directory. #]=======================================================================] include(${CMAKE_SOURCE_DIR}/cmake/GRASSUtilities.cmake) set(PROJ_INCLUDE_DIRS) set(PROJ_FOUND) if(PROJ_INCLUDE_DIR) # if PROJ_INCLUDE_DIR is explicitly set, use it set(PROJ_INCLUDE_DIRS ${PROJ_INCLUDE_DIR}) set(PROJ_FOUND TRUE) else() # try to use GRASS' settings get_grass_platform_var("${GRASS_GISBASE}/include/Make/Platform.make" "PROJINC" PROJ_INCLUDE_DIRS) if(PROJ_INCLUDE_DIRS) string(REGEX REPLACE "-I(.*)" "\\1" PROJ_INCLUDE_DIRS "${PROJ_INCLUDE_DIRS}") set(PROJ_FOUND TRUE) endif() endif() if(NOT PROJ_FOUND) find_package(PROJ CONFIG) endif() if(NOT PROJ_FOUND) find_path(PROJ_INCLUDE_DIRS NAMES proj_api.h proj.h) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PROJ REQUIRED_VARS PROJ_INCLUDE_DIRS PROJ_FOUND) gdal-grass-2.0.0/cmake/FindPostgreSQL.cmake000066400000000000000000000057621510331751700204400ustar00rootroot00000000000000#[=======================================================================[.rst: FindPostgreSQL -------------- Find PostgreSQL's include path. Result Variables ^^^^^^^^^^^^^^^^ This module will set the following variables in your project: ``PostgreSQL_FOUND`` True if PostgreSQL is found. ``PostgreSQL_INCLUDE_DIRS`` the directories of the PostgreSQL headers Hints ^^^^^ Set ``PostgreSQL_INCLUDE_DIR`` environment variable to specify the location of non-standard location of PostgreSQL include directory. #]=======================================================================] # check out GRASS' settings include(${CMAKE_SOURCE_DIR}/cmake/GRASSUtilities.cmake) get_grass_platform_var("${GRASS_GISBASE}/include/Make/Platform.make" "PQINCPATH" PQINCPATH) get_grass_platform_var("${GRASS_GISBASE}/include/Make/Platform.make" "USE_POSTGRES" USE_POSTGRES) if(PQINCPATH) string(REGEX REPLACE "-I(.*)" "\\1" PQINCPATH "${PQINCPATH}") endif() if(PostgreSQL_INCLUDE_DIR) # if PostgreSQL_INCLUDE_DIR is explicitly set, use it set(PostgreSQL_FOUND TRUE) elseif(USE_POSTGRES AND PQINCPATH) # Use GRASS' settings set(PostgreSQL_INCLUDE_DIR ${PQINCPATH}) set(PostgreSQL_FOUND TRUE) endif() if(NOT PostgreSQL_FOUND) set(PostgreSQL_KNOWN_VERSIONS ${PostgreSQL_ADDITIONAL_VERSIONS} "16" "15" "14" "13" "12" "11" "10" "9.6" "9.5" "9.4" "9.3" "9.2" "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0") # Define additional search paths for root directories. set( PostgreSQL_ROOT_DIRECTORIES ENV PostgreSQL_ROOT ${PostgreSQL_ROOT} ) foreach(suffix ${PostgreSQL_KNOWN_VERSIONS}) if(WIN32) list(APPEND PostgreSQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES "PostgreSQL/${suffix}/lib") list(APPEND PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES "PostgreSQL/${suffix}/include") list(APPEND PostgreSQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES "PostgreSQL/${suffix}/include/server") endif() if(UNIX) list(APPEND PostgreSQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES "postgresql${suffix}" "postgresql@${suffix}" "pgsql-${suffix}/lib") list(APPEND PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES "postgresql${suffix}" "postgresql@${suffix}" "postgresql/${suffix}" "pgsql-${suffix}/include") list(APPEND PostgreSQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES "postgresql${suffix}/server" "postgresql@${suffix}/server" "postgresql/${suffix}/server" "pgsql-${suffix}/include/server") endif() endforeach() find_path(PostgreSQL_INCLUDE_DIR NAMES libpq-fe.h PATHS # Look in other places. ${PostgreSQL_ROOT_DIRECTORIES} PATH_SUFFIXES pgsql postgresql include ${PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES} ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PostgreSQL REQUIRED_VARS PostgreSQL_INCLUDE_DIR PostgreSQL_FOUND) if(PostgreSQL_FOUND) set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR}) endif() gdal-grass-2.0.0/cmake/GRASSUtilities.cmake000066400000000000000000000010311510331751700203700ustar00rootroot00000000000000# Get definition from GRASS' Platform.make file function(get_grass_platform_var platform_file searchvar outvar) set(def "") file(STRINGS "${platform_file}" lines REGEX ".* = .*") foreach(line IN LISTS lines) string(REGEX REPLACE "(.*) = .*" "\\1" var "${line}") string(REGEX REPLACE ".* = (.*)" "\\1" def "${line}") string(STRIP "${var}" var) string(STRIP "${def}" def) if("${searchvar}" STREQUAL "${var}") set(${outvar} "${def}" PARENT_SCOPE) endif() endforeach() endfunction() gdal-grass-2.0.0/cmake/MacPlistMacros.cmake000066400000000000000000000012361510331751700205050ustar00rootroot00000000000000# Mac Plist Macros function(get_version_plist plistfile outvar) set(pversion "") if(EXISTS ${plistfile}) file(READ "${plistfile}" info_plist) string(REGEX REPLACE "\n" "" info_plist "${info_plist}") string( REGEX MATCH "CFBundleShortVersionString[ \t]*([0-9\\.]*)" PLISTVERSION "${info_plist}") string( REGEX REPLACE "CFBundleShortVersionString[ \t]*([0-9\\.]*)" "\\1" pversion "${PLISTVERSION}") endif(EXISTS ${plistfile}) set(${outvar} ${pversion} PARENT_SCOPE) endfunction(get_version_plist) gdal-grass-2.0.0/docs/000077500000000000000000000000001510331751700144705ustar00rootroot00000000000000gdal-grass-2.0.0/docs/grass_raster.md000066400000000000000000000054711510331751700175200ustar00rootroot00000000000000# GDAL-GRASS driver: Raster Format GDAL optionally supports reading of existing GRASS GIS raster maps or imagery groups, but not writing or export. The support for GRASS raster format is determined when the library is configured, and requires libgrass to be pre-installed (see Notes below). GRASS raster maps/imagery groups can be selected in several ways. 1. The full path to the `cellhd` file can be specified. This is not a relative path, or at least it must contain all the path components within the GRASS database including the database root itself. The following example opens the raster map "elevation" within the GRASS mapset "PERMANENT" of the GRASS location "myloc" in the GRASS database located at `/data/grassdb`. For example: gdalinfo /data/grassdb/myloc/PERMANENT/cellhd/elevation 2. The full path to the directory containing information about an imagery group (or the REF file within it) can be specified to refer to the whole group as a single dataset. The following examples do the same thing. For example: gdalinfo /data/grassdb/imagery/raw/group/testmff/REF gdalinfo /data/grassdb/imagery/raw/group/testmff 3. If there is a correct `.grassrc7/rc` (GRASS 7) setup file in the user's home directory then raster maps or imagery groups may be opened just by the cell or group name. This only works for raster maps or imagery groups in the current GRASS location and mapset as defined in the GRASS setup file. The following features are supported by the GDAL/GRASS link. - Up to 256 entries from raster colormaps are read (0-255). - Compressed and uncompressed integer (CELL), floating point (FCELL) and double precision (DCELL) raster maps are all supported. Integer raster maps are classified with a band type of "Byte" if the 1-byte per pixel format is used, or "UInt16" if the two byte per pixel format is used. Otherwise integer raster maps are treated as "UInt32". - Georeferencing information is properly read from GRASS format. - An attempt is made to translate coordinate systems, but some conversions may be flawed, in particular in handling of datums and units. ## Driver capabilities ## Notes on driver variations The driver is able to use the GRASS GIS shared libraries directly instead of using libgrass (not recommended due to potentially circular dependencies). Currently both versions of the driver are available and can be configured using `--with-libgrass` for the libgrass variant or `--with-grass=` for the GRASS GIS library based version. The GRASS driver version currently does not support coordinate system access, though it is hoped that will be corrected at some point. ## See Also - [GRASS GIS home page](https://grass.osgeo.org) - [old libgrass page](https://web.archive.org/web/20130730111701/http://home.gdal.org/projects/grass/) gdal-grass-2.0.0/docs/grass_vector.md000066400000000000000000000105501510331751700175140ustar00rootroot00000000000000# GDAL-GRASS driver: Vector Format The GDAL-GRASS driver can read GRASS (version 6.0 and higher) vector maps. Each GRASS vector map is represented as one datasource. A GRASS vector map may have 0, 1 or more layers. GRASS points are represented as wkbPoint, lines and boundaries as wkbLineString and areas as wkbPolygon. wkbMulti\* and wkbGeometryCollection are not used. More feature types can be mixed in one layer. If a layer contains only features of one type, it is set appropriately and can be retrieved by OGRLayer::GetLayerDefn(); If a geometry has more categories of the same layer attached, its represented as more features (one for each category). Both 2D and 3D maps are supported. ## Driver capabilities ## Datasource name Datasource name is full path to 'head' file in GRASS vector directory. Using names of GRASS environment variables it can be expressed: $GISDBASE/$LOCATION_NAME/$MAPSET/vector/mymap/head where 'mymap' is name of a vector map. For example: /home/cimrman/grass_data/jizerky/jara/vector/liptakov/head ## Layer names Usually layer numbers are used as layer names. Layer number 0 is used for all features without any category. It is possible to optionally give names to GRASS layers linked to database however currently this is not supported by grass modules. A layer name can be added in 'dbln' vector file as '/name' after layer number, for example to original record: 1 rivers cat $GISDBASE/$LOCATION_NAME/$MAPSET/dbf/ dbf it is possible to assign name 'rivers' 1/rivers rivers cat $GISDBASE/$LOCATION_NAME/$MAPSET/dbf/ dbf the layer 1 will be listed is layer 'rivers'. ## Attribute filter If a layer has attributes stored in a database, the query is passed to the underlying database driver. That means, that SQL conditions which can be used depend on the driver and database to which the layer is linked. For example, DBF driver has currently very limited set of SQL expressions and PostgreSQL offers very rich set of SQL expressions. If a layer has no attributes linked and it has only categories, OGR internal SQL engine is used to evaluate the expression. Category is an integer number attached to geometry, it is sort of ID, but it is not FID as more features in one layer can have the same category. Evaluation is done once when the attribute filter is set. ## Spatial filter Bounding boxes of features stored in topology structure are used to evaluate if a features matches current spatial filter. Evaluation is done once when the spatial filter is set. ## GISBASE GISBASE is full path to the directory where GRASS is installed. By default, GRASS driver is using the path given to gdal configure script. A different directory can be forced by setting GISBASE environment variable. GISBASE is used to find GRASS database drivers. ## Missing topology GRASS driver can read GRASS vector files if topology is available (AKA level 2). If an error is reported, telling that the topology is not available, it is necessary to build topology within GRASS using v.build module. ## Random access If random access (GetFeature instead of GetNextFeature) is used on layer with attributes, the reading of features can be quite slow. It is because the driver has to query attributes by category for each feature (to avoid using a lot of memory) and random access to database is usually slow. This can be improved on GRASS side optimizing/writing file based (DBF, SQLite) drivers. ## Known problem Because of bug in GRASS library, it is impossible to start/stop database drivers in FIFO order and FILO order must be used. The GRASS driver for OGR is written with this limit in mind and drivers are always closed if not used and if a driver remains opened kill() is used to terminate it. It can happen however in rare cases, that the driver will try to stop database driver which is not the last opened and an application hangs. This can happen if sequential read (GetNextFeature) of a layer is not finished (reading is stopped before last available feature is reached), features from another layer are read and then the reading of the first layer is finished, because in that case kill() is not used. ## See Also - [GRASS GIS home page](https://grass.osgeo.org) - [old libgrass page](https://web.archive.org/web/20130730111701/http://home.gdal.org/projects/grass/) ----- Development of this driver was financially supported by Faunalia ([www.faunalia.it](http://www.faunalia.it/)). gdal-grass-2.0.0/renovate.json000066400000000000000000000001531510331751700162550ustar00rootroot00000000000000{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ] } gdal-grass-2.0.0/source/000077500000000000000000000000001510331751700150405ustar00rootroot00000000000000gdal-grass-2.0.0/source/grass.cpp000066400000000000000000001077161510331751700166770ustar00rootroot00000000000000/****************************************************************************** * * Project: GRASS Driver * Purpose: Implement GRASS raster read/write support * This version is for GRASS GIS 7+ and uses GRASS libraries * directly instead of using libgrass. * Author: Frank Warmerdam * Radim Blazek * ****************************************************************************** * Copyright (c) 2000 Frank Warmerdam * Copyright (c) 2007-2020, Even Rouault * * SPDX-License-Identifier: MIT * ****************************************************************************/ #include #include #include "cpl_string.h" #include "gdal_frmts.h" #include "gdal_priv.h" #include "ogr_spatialref.h" extern "C" { #ifdef __cplusplus #define class _class #endif #include #ifdef __cplusplus #undef class #endif #include #include #include auto GPJ_grass_to_wkt(const struct Key_Value *, const struct Key_Value *, int, int) -> char *; void GDALRegister_GRASS(); } enum { BUFF_SIZE = 200, GRASS_MAX_COLORS = 100000 }; /************************************************************************/ /* Grass2CPLErrorHook() */ /************************************************************************/ static auto Grass2CPLErrorHook(char *pszMessage, int bFatal) -> int { if (!bFatal) //CPLDebug( "GRASS", "%s", pszMessage ); CPLError(CE_Warning, CPLE_AppDefined, "GRASS warning: %s", pszMessage); else CPLError(CE_Warning, CPLE_AppDefined, "GRASS fatal error: %s", pszMessage); return 0; } struct GRASSRasterPath { std::string gisdbase; std::string location; std::string mapset; std::string element; std::string name; explicit GRASSRasterPath(const char *path); auto isValid() -> bool; auto isCellHD() -> bool; }; /************************************************************************/ /* ==================================================================== */ /* GRASSDataset */ /* ==================================================================== */ /************************************************************************/ class GRASSRasterBand; class GRASSDataset final : public GDALDataset { friend class GRASSRasterBand; std::string osGisdbase; std::string osLocation; /* LOCATION_NAME */ std::string osElement; /* cellhd or group */ struct Cell_head sCellInfo { }; /* raster region */ OGRSpatialReference m_oSRS{}; #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 12, 0) GDALGeoTransform m_gt{}; #else std::array m_gt{0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; #endif public: explicit GRASSDataset(GRASSRasterPath &); auto GetSpatialRef() const -> const OGRSpatialReference * override; #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 12, 0) auto GetGeoTransform(GDALGeoTransform &) const -> CPLErr override; #else auto GetGeoTransform(double *) -> CPLErr override; #endif static auto Open(GDALOpenInfo *) -> GDALDataset *; }; /************************************************************************/ /* ==================================================================== */ /* GRASSRasterBand */ /* ==================================================================== */ /************************************************************************/ class GRASSRasterBand final : public GDALRasterBand { friend class GRASSDataset; std::string osCellName; std::string osMapset; int hCell; int nGRSType; // GRASS raster type: CELL_TYPE, FCELL_TYPE, DCELL_TYPE bool nativeNulls; // use GRASS native NULL values struct Colors sGrassColors { }; GDALColorTable *poCT; struct Cell_head sOpenWindow { }; /* the region when the raster was opened */ int bHaveMinMax; double dfCellMin{0.0}; double dfCellMax{0.0}; double dfNoData; bool valid{false}; public: GRASSRasterBand(GRASSDataset *, int, std::string &, std::string &); ~GRASSRasterBand() override; auto IReadBlock(int, int, void *) -> CPLErr override; auto IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int, GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg) -> CPLErr override; auto GetColorInterpretation() -> GDALColorInterp override; auto GetColorTable() -> GDALColorTable * override; auto GetMinimum(int *pbSuccess = nullptr) -> double override; auto GetMaximum(int *pbSuccess = nullptr) -> double override; auto GetNoDataValue(int *pbSuccess = nullptr) -> double override; private: void SetWindow(struct Cell_head *); auto ResetReading(struct Cell_head *) -> CPLErr; }; /************************************************************************/ /* GRASSRasterBand() */ /************************************************************************/ GRASSRasterBand::GRASSRasterBand(GRASSDataset *poDSIn, int nBandIn, std::string &pszMapsetIn, std::string &pszCellNameIn) : osCellName(pszCellNameIn), osMapset(pszMapsetIn), nGRSType(Rast_map_type(osCellName.c_str(), osMapset.c_str())) { struct Cell_head sCellInfo { }; // Note: GISDBASE, LOCATION_NAME ans MAPSET was set in GRASSDataset::Open this->poDS = poDSIn; this->nBand = nBandIn; Rast_get_cellhd(osCellName.c_str(), osMapset.c_str(), &sCellInfo); /* -------------------------------------------------------------------- */ /* Get min/max values. */ /* -------------------------------------------------------------------- */ struct FPRange sRange { }; if (Rast_read_fp_range(osCellName.c_str(), osMapset.c_str(), &sRange) == -1) { bHaveMinMax = FALSE; } else { bHaveMinMax = TRUE; Rast_get_fp_range_min_max(&sRange, &dfCellMin, &dfCellMax); } /* -------------------------------------------------------------------- */ /* Setup band type, and preferred nodata value. */ /* -------------------------------------------------------------------- */ // Negative values are also (?) stored as 4 bytes (format = 3) // => raster with format < 3 has only positive values // GRASS modules usually do not waste space and only the format necessary to keep // full raster values range is used -> no checks if shorter type could be used if (nGRSType == CELL_TYPE) { if (sCellInfo.format == 0) { // 1 byte / cell -> possible range 0,255 if (bHaveMinMax && dfCellMin > 0) { this->eDataType = GDT_Byte; dfNoData = 0.0; } else if (bHaveMinMax && dfCellMax < 255) { this->eDataType = GDT_Byte; dfNoData = 255.0; } else { // maximum is not known or full range is used this->eDataType = GDT_UInt16; dfNoData = 256.0; } nativeNulls = false; } else if (sCellInfo.format == 1) { // 2 bytes / cell -> possible range 0,65535 if (bHaveMinMax && dfCellMin > 0) { this->eDataType = GDT_UInt16; dfNoData = 0.0; } else if (bHaveMinMax && dfCellMax < 65535) { this->eDataType = GDT_UInt16; dfNoData = 65535; } else { // maximum is not known or full range is used CELL cval = 0; this->eDataType = GDT_Int32; Rast_set_c_null_value(&cval, 1); dfNoData = (double)cval; } nativeNulls = false; } else { // 3-4 bytes CELL cval = 0; this->eDataType = GDT_Int32; Rast_set_c_null_value(&cval, 1); dfNoData = (double)cval; nativeNulls = true; } } else if (nGRSType == FCELL_TYPE) { FCELL fval = NAN; this->eDataType = GDT_Float32; Rast_set_f_null_value(&fval, 1); dfNoData = (double)fval; nativeNulls = true; } else if (nGRSType == DCELL_TYPE) { DCELL dval = NAN; this->eDataType = GDT_Float64; Rast_set_d_null_value(&dval, 1); dfNoData = (double)dval; nativeNulls = true; } nBlockXSize = poDSIn->nRasterXSize; nBlockYSize = 1; Rast_set_window(&(poDSIn->sCellInfo)); // open the raster only for actual reading hCell = -1; memcpy(static_cast(&sOpenWindow), static_cast(&(poDSIn->sCellInfo)), sizeof(struct Cell_head)); /* -------------------------------------------------------------------- */ /* Do we have a color table? */ /* -------------------------------------------------------------------- */ poCT = nullptr; if (Rast_read_colors(osCellName.c_str(), osMapset.c_str(), &sGrassColors) == 1) { int maxcolor = 0; CELL min = 0, max = 0; Rast_get_c_color_range(&min, &max, &sGrassColors); if (bHaveMinMax) { if (max < dfCellMax) { maxcolor = max; } else { maxcolor = (int)ceil(dfCellMax); } if (maxcolor > GRASS_MAX_COLORS) { maxcolor = GRASS_MAX_COLORS; CPLDebug("GRASS", "Too many values, color table cut to %d entries.", maxcolor); } } else { if (max < GRASS_MAX_COLORS) { maxcolor = max; } else { maxcolor = GRASS_MAX_COLORS; CPLDebug("GRASS", "Too many values, color table set to %d entries.", maxcolor); } } poCT = new GDALColorTable(); for (int iColor = 0; iColor <= maxcolor; iColor++) { int nRed = 0, nGreen = 0, nBlue = 0; GDALColorEntry sColor; if (Rast_get_c_color(&iColor, &nRed, &nGreen, &nBlue, &sGrassColors)) { sColor.c1 = (short)nRed; sColor.c2 = (short)nGreen; sColor.c3 = (short)nBlue; sColor.c4 = 255; poCT->SetColorEntry(iColor, &sColor); } else { sColor.c1 = 0; sColor.c2 = 0; sColor.c3 = 0; sColor.c4 = 0; poCT->SetColorEntry(iColor, &sColor); } } /* Create metadata entries for color table rules */ std::array value{}; std::array key{}; int rcount = Rast_colors_count(&sGrassColors); (void)std::snprintf(value.data(), BUFF_SIZE, "%d", rcount); this->SetMetadataItem("COLOR_TABLE_RULES_COUNT", value.data()); /* Add the rules in reverse order */ for (int i = rcount - 1; i >= 0; i--) { DCELL val1 = NAN, val2 = NAN; unsigned char r1 = 0, g1 = 0, b1 = 0, r2 = 0, g2 = 0, b2 = 0; Rast_get_fp_color_rule(&val1, &r1, &g1, &b1, &val2, &r2, &g2, &b2, &sGrassColors, i); (void)std::snprintf(key.data(), key.size(), "COLOR_TABLE_RULE_RGB_%d", rcount - i - 1); (void)std::snprintf(value.data(), value.size(), "%e %e %d %d %d %d %d %d", val1, val2, r1, g1, b1, r2, g2, b2); this->SetMetadataItem(key.data(), value.data()); } } else { this->SetMetadataItem("COLOR_TABLE_RULES_COUNT", "0"); } this->valid = true; } /************************************************************************/ /* ~GRASSRasterBand() */ /************************************************************************/ GRASSRasterBand::~GRASSRasterBand() { if (poCT != nullptr) { Rast_free_colors(&sGrassColors); delete poCT; } if (hCell >= 0) Rast_close(hCell); } /************************************************************************/ /* SetWindow */ /* */ /* Helper for ResetReading */ /* close the current GRASS raster band, actually set the new window, */ /* reset GRASS variables */ /* */ /* Returns nothing */ /************************************************************************/ void GRASSRasterBand::SetWindow(struct Cell_head *sNewWindow) { if (hCell >= 0) { Rast_close(hCell); hCell = -1; } /* Set window */ Rast_set_window(sNewWindow); /* Set GRASS env to the current raster, don't open the raster */ G_setenv_nogisrc("GISDBASE", (dynamic_cast(poDS))->osGisdbase.c_str()); G_setenv_nogisrc("LOCATION_NAME", (dynamic_cast(poDS))->osLocation.c_str()); G_setenv_nogisrc("MAPSET", osMapset.c_str()); G_reset_mapsets(); G_add_mapset_to_search_path(osMapset.c_str()); } /************************************************************************/ /* ResetReading */ /* */ /* Reset current window for a new reading request, */ /* close the current GRASS raster band, reset GRASS variables */ /* */ /* Returns CE_Failure if fails, otherwise CE_None */ /************************************************************************/ auto GRASSRasterBand::ResetReading(struct Cell_head *sNewWindow) -> CPLErr { /* Check if the window has changed */ if (sNewWindow->north != sOpenWindow.north || sNewWindow->south != sOpenWindow.south || sNewWindow->east != sOpenWindow.east || sNewWindow->west != sOpenWindow.west || sNewWindow->ew_res != sOpenWindow.ew_res || sNewWindow->ns_res != sOpenWindow.ns_res || sNewWindow->rows != sOpenWindow.rows || sNewWindow->cols != sOpenWindow.cols) { SetWindow(sNewWindow); memcpy(static_cast(&sOpenWindow), static_cast(sNewWindow), sizeof(struct Cell_head)); } else { /* The windows are identical, check current window */ struct Cell_head sCurrentWindow { }; Rast_get_window(&sCurrentWindow); if (sNewWindow->north != sCurrentWindow.north || sNewWindow->south != sCurrentWindow.south || sNewWindow->east != sCurrentWindow.east || sNewWindow->west != sCurrentWindow.west || sNewWindow->ew_res != sCurrentWindow.ew_res || sNewWindow->ns_res != sCurrentWindow.ns_res || sNewWindow->rows != sCurrentWindow.rows || sNewWindow->cols != sCurrentWindow.cols) { SetWindow(sNewWindow); } } return CE_None; } /************************************************************************/ /* IReadBlock() */ /* */ /************************************************************************/ auto GRASSRasterBand::IReadBlock(int /*nBlockXOff*/, int nBlockYOff, void *pImage) -> CPLErr { if (!this->valid) return CE_Failure; // Reset window because IRasterIO could be previously called. if (ResetReading(&((dynamic_cast(poDS))->sCellInfo)) != CE_None) { return CE_Failure; } // open for reading if (hCell < 0) { hCell = Rast_open_old(osCellName.c_str(), osMapset.c_str()); if (hCell < 0) { CPLError(CE_Failure, CPLE_AppDefined, "GRASS: Cannot open raster '%s'", osCellName.c_str()); return CE_Failure; } } if (eDataType == GDT_Byte || eDataType == GDT_UInt16) { CELL *cbuf = Rast_allocate_c_buf(); Rast_get_c_row(hCell, cbuf, nBlockYOff); /* Reset NULLs */ for (int col = 0; col < nBlockXSize; col++) { if (Rast_is_c_null_value(&(cbuf[col]))) cbuf[col] = (CELL)dfNoData; } GDALCopyWords(static_cast(cbuf), GDT_Int32, sizeof(CELL), pImage, eDataType, GDALGetDataTypeSizeBytes(eDataType), nBlockXSize); G_free(cbuf); } else if (eDataType == GDT_Int32) { Rast_get_c_row(hCell, static_cast(pImage), nBlockYOff); } else if (eDataType == GDT_Float32) { Rast_get_f_row(hCell, static_cast(pImage), nBlockYOff); } else if (eDataType == GDT_Float64) { Rast_get_d_row(hCell, static_cast(pImage), nBlockYOff); } // close to avoid confusion with other GRASS raster bands Rast_close(hCell); hCell = -1; return CE_None; } /************************************************************************/ /* IRasterIO() */ /* */ /************************************************************************/ auto GRASSRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg * /*psExtraArg*/) -> CPLErr { /* GRASS library does that, we have only calculate and reset the region in map units * and if the region has changed, reopen the raster */ /* Calculate the region */ struct Cell_head sWindow { }; struct Cell_head *psDsWindow = nullptr; if (eRWFlag != GF_Read) return CE_Failure; if (!this->valid) return CE_Failure; psDsWindow = &((dynamic_cast(poDS))->sCellInfo); sWindow.north = psDsWindow->north - nYOff * psDsWindow->ns_res; sWindow.south = sWindow.north - nYSize * psDsWindow->ns_res; sWindow.west = psDsWindow->west + nXOff * psDsWindow->ew_res; sWindow.east = sWindow.west + nXSize * psDsWindow->ew_res; sWindow.proj = psDsWindow->proj; sWindow.zone = psDsWindow->zone; sWindow.cols = nBufXSize; sWindow.rows = nBufYSize; /* Reset resolution */ G_adjust_Cell_head(&sWindow, 1, 1); if (ResetReading(&sWindow) != CE_None) { return CE_Failure; } // open for reading if (hCell < 0) { hCell = Rast_open_old(osCellName.c_str(), osMapset.c_str()); if (hCell < 0) { CPLError(CE_Failure, CPLE_AppDefined, "GRASS: Cannot open raster '%s'", osCellName.c_str()); return CE_Failure; } } /* Read Data */ CELL *cbuf = nullptr; FCELL *fbuf = nullptr; DCELL *dbuf = nullptr; bool direct = false; /* Reset space if default (0) */ if (nPixelSpace == 0) nPixelSpace = GDALGetDataTypeSizeBytes(eBufType); if (nLineSpace == 0) nLineSpace = nBufXSize * nPixelSpace; if (nGRSType == CELL_TYPE && (!nativeNulls || eBufType != GDT_Int32 || sizeof(CELL) != 4 || nPixelSpace != sizeof(CELL))) { cbuf = Rast_allocate_c_buf(); } else if (nGRSType == FCELL_TYPE && (eBufType != GDT_Float32 || nPixelSpace != sizeof(FCELL))) { fbuf = Rast_allocate_f_buf(); } else if (nGRSType == DCELL_TYPE && (eBufType != GDT_Float64 || nPixelSpace != sizeof(DCELL))) { dbuf = Rast_allocate_d_buf(); } else { direct = true; } for (int row = 0; row < nBufYSize; row++) { char *pnt = static_cast(pData) + row * nLineSpace; if (nGRSType == CELL_TYPE) { if (direct) { Rast_get_c_row(hCell, reinterpret_cast(pnt), row); } else { Rast_get_c_row(hCell, cbuf, row); /* Reset nullptrs */ for (int col = 0; col < nBufXSize; col++) { if (Rast_is_c_null_value(&(cbuf[col]))) cbuf[col] = (CELL)dfNoData; } GDALCopyWords(static_cast(cbuf), GDT_Int32, sizeof(CELL), static_cast(pnt), eBufType, (int)nPixelSpace, nBufXSize); } } else if (nGRSType == FCELL_TYPE) { if (direct) { Rast_get_f_row(hCell, reinterpret_cast(pnt), row); } else { Rast_get_f_row(hCell, fbuf, row); GDALCopyWords(static_cast(fbuf), GDT_Float32, sizeof(FCELL), static_cast(pnt), eBufType, (int)nPixelSpace, nBufXSize); } } else if (nGRSType == DCELL_TYPE) { if (direct) { Rast_get_d_row(hCell, reinterpret_cast(pnt), row); } else { Rast_get_d_row(hCell, dbuf, row); GDALCopyWords(static_cast(dbuf), GDT_Float64, sizeof(DCELL), static_cast(pnt), eBufType, (int)nPixelSpace, nBufXSize); } } } if (cbuf) G_free(cbuf); if (fbuf) G_free(fbuf); if (dbuf) G_free(dbuf); // close to avoid confusion with other GRASS raster bands Rast_close(hCell); hCell = -1; return CE_None; } /************************************************************************/ /* GetColorInterpretation() */ /************************************************************************/ auto GRASSRasterBand::GetColorInterpretation() -> GDALColorInterp { if (poCT != nullptr) return GCI_PaletteIndex; else return GCI_GrayIndex; } /************************************************************************/ /* GetColorTable() */ /************************************************************************/ auto GRASSRasterBand::GetColorTable() -> GDALColorTable * { return poCT; } /************************************************************************/ /* GetMinimum() */ /************************************************************************/ auto GRASSRasterBand::GetMinimum(int *pbSuccess) -> double { if (pbSuccess) *pbSuccess = bHaveMinMax; if (bHaveMinMax) return dfCellMin; else if (eDataType == GDT_Float32 || eDataType == GDT_Float64) return -4294967295.0; else return 0; } /************************************************************************/ /* GetMaximum() */ /************************************************************************/ auto GRASSRasterBand::GetMaximum(int *pbSuccess) -> double { if (pbSuccess) *pbSuccess = bHaveMinMax; if (bHaveMinMax) return dfCellMax; else if (eDataType == GDT_Float32 || eDataType == GDT_Float64 || eDataType == GDT_UInt32) return 4294967295.0; else if (eDataType == GDT_UInt16) return 65535; else return 255; } /************************************************************************/ /* GetNoDataValue() */ /************************************************************************/ auto GRASSRasterBand::GetNoDataValue(int *pbSuccess) -> double { if (pbSuccess) *pbSuccess = TRUE; return dfNoData; } /************************************************************************/ /* ==================================================================== */ /* GRASSDataset */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* GRASSDataset() */ /************************************************************************/ GRASSDataset::GRASSDataset(GRASSRasterPath &gpath) : osGisdbase(gpath.gisdbase), osLocation(gpath.location), osElement(gpath.element) { m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); } /************************************************************************/ /* GetSpatialRef() */ /************************************************************************/ auto GRASSDataset::GetSpatialRef() const -> const OGRSpatialReference * { return m_oSRS.IsEmpty() ? nullptr : &m_oSRS; } /************************************************************************/ /* GetGeoTransform() */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 12, 0) auto GRASSDataset::GetGeoTransform(GDALGeoTransform >) const -> CPLErr { gt = m_gt; return CE_None; } #else auto GRASSDataset::GetGeoTransform(double *padfGeoTransform) -> CPLErr { memcpy(padfGeoTransform, m_gt.data(), sizeof(double) * 6); return CE_None; } #endif /************************************************************************/ /* Open() */ /************************************************************************/ using GrassErrorHandler = auto(*)(const char *, int) -> int; auto GRASSDataset::Open(GDALOpenInfo *poOpenInfo) -> GDALDataset * { char **papszCells = nullptr; char **papszMapsets = nullptr; /* -------------------------------------------------------------------- */ /* Does this even look like a grass file path? */ /* -------------------------------------------------------------------- */ if (strstr(poOpenInfo->pszFilename, "/cellhd/") == nullptr && strstr(poOpenInfo->pszFilename, "/group/") == nullptr) return nullptr; /* Always init, if no rasters are opened G_no_gisinit resets the projection and * rasters in different projection may be then opened */ // Don't use GISRC file and read/write GRASS variables (from location G_VAR_GISRC) to memory only. G_set_gisrc_mode(G_GISRC_MODE_MEMORY); // Init GRASS libraries (required) G_no_gisinit(); // Doesn't check write permissions for mapset compare to G_gisinit // Set error function G_set_error_routine(GrassErrorHandler(Grass2CPLErrorHook)); // GISBASE is path to the directory where GRASS is installed, if (!getenv("GISBASE")) { static char *gisbaseEnv = nullptr; const char *gisbase = GRASS_GISBASE; CPLError(CE_Warning, CPLE_AppDefined, "GRASS warning: GISBASE " "environment variable was not set, using:\n%s", gisbase); std::array buf{}; int res = std::snprintf(buf.data(), BUFF_SIZE, "GISBASE=%s", gisbase); if (res >= BUFF_SIZE) { CPLError( CE_Warning, CPLE_AppDefined, "GRASS warning: GISBASE environment variable was too long.\n"); return nullptr; } CPLFree(gisbaseEnv); gisbaseEnv = CPLStrdup(buf.data()); putenv(gisbaseEnv); } GRASSRasterPath gp = GRASSRasterPath(poOpenInfo->pszFilename); /* -------------------------------------------------------------------- */ /* Check element name */ /* -------------------------------------------------------------------- */ if (!gp.isValid()) { return nullptr; } /* -------------------------------------------------------------------- */ /* Set GRASS variables */ /* -------------------------------------------------------------------- */ G_setenv_nogisrc("GISDBASE", gp.gisdbase.c_str()); G_setenv_nogisrc("LOCATION_NAME", gp.location.c_str()); G_setenv_nogisrc( "MAPSET", gp.mapset.c_str()); // group is searched only in current mapset G_reset_mapsets(); G_add_mapset_to_search_path(gp.mapset.c_str()); /* -------------------------------------------------------------------- */ /* Check if this is a valid grass cell. */ /* -------------------------------------------------------------------- */ if (gp.isCellHD()) { if (G_find_file2("cell", gp.name.c_str(), gp.mapset.c_str()) == nullptr) { return nullptr; } papszMapsets = CSLAddString(papszMapsets, gp.mapset.c_str()); papszCells = CSLAddString(papszCells, gp.name.c_str()); } /* -------------------------------------------------------------------- */ /* Check if this is a valid GRASS imagery group. */ /* -------------------------------------------------------------------- */ else { struct Ref ref { }; I_init_group_ref(&ref); bool has_group_ref = I_get_group_ref(gp.name.c_str(), &ref); if (!has_group_ref || ref.nfiles <= 0) { return nullptr; } for (int iRef = 0; iRef < ref.nfiles; iRef++) { papszCells = CSLAddString(papszCells, ref.file[iRef].name); papszMapsets = CSLAddString(papszMapsets, ref.file[iRef].mapset); G_add_mapset_to_search_path(ref.file[iRef].mapset); } I_free_group_ref(&ref); } /* -------------------------------------------------------------------- */ /* Create a corresponding GDALDataset. */ /* -------------------------------------------------------------------- */ auto poDS = new GRASSDataset(gp); /* notdef: should only allow read access to an existing cell, right? */ poDS->eAccess = poOpenInfo->eAccess; if (!papszCells) { return nullptr; } /* -------------------------------------------------------------------- */ /* Capture some information from the file that is of interest. */ /* -------------------------------------------------------------------- */ Rast_get_cellhd(papszCells[0], papszMapsets[0], &(poDS->sCellInfo)); poDS->nRasterXSize = poDS->sCellInfo.cols; poDS->nRasterYSize = poDS->sCellInfo.rows; poDS->m_gt[0] = poDS->sCellInfo.west; poDS->m_gt[1] = poDS->sCellInfo.ew_res; poDS->m_gt[2] = 0.0; poDS->m_gt[3] = poDS->sCellInfo.north; poDS->m_gt[4] = 0.0; poDS->m_gt[5] = -1 * poDS->sCellInfo.ns_res; /* -------------------------------------------------------------------- */ /* Try to get a projection definition. */ /* -------------------------------------------------------------------- */ struct Key_Value *projinfo = G_get_projinfo(); struct Key_Value *projunits = G_get_projunits(); char *pszWKT = GPJ_grass_to_wkt(projinfo, projunits, 0, 0); if (projinfo) G_free_key_value(projinfo); if (projunits) G_free_key_value(projunits); if (pszWKT) poDS->m_oSRS.importFromWkt(pszWKT); G_free(pszWKT); /* -------------------------------------------------------------------- */ /* Create band information objects. */ /* -------------------------------------------------------------------- */ for (int iBand = 0; papszCells[iBand] != nullptr; iBand++) { std::string msets = std::string(papszMapsets[iBand]); std::string cells = std::string(papszCells[iBand]); auto rb = new GRASSRasterBand(poDS, iBand + 1, msets, cells); if (!rb->valid) { CPLError(CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster band %d", iBand); delete rb; delete poDS; return nullptr; } poDS->SetBand(iBand + 1, rb); } CSLDestroy(papszCells); CSLDestroy(papszMapsets); /* -------------------------------------------------------------------- */ /* Confirm the requested access is supported. */ /* -------------------------------------------------------------------- */ if (poOpenInfo->eAccess == GA_Update) { delete poDS; CPLError(CE_Failure, CPLE_NotSupported, "The GRASS driver does not support update access to existing" " datasets.\n"); return nullptr; } return poDS; } /************************************************************************/ /* GRASSRasterPath */ /************************************************************************/ GRASSRasterPath::GRASSRasterPath(const char *path) { if (!path || std::strlen(path) == 0) return; char *p = nullptr; std::array ptr{}; int i = 0; auto tmp = std::unique_ptr(new char[std::strlen(path) + 1]); std::strcpy(tmp.get(), path); while ((p = std::strrchr(tmp.get(), '/')) != nullptr && i < 4) { *p = '\0'; if (std::strlen(p + 1) == 0) /* repeated '/' */ continue; ptr[i++] = p + 1; } /* Note: empty GISDBASE == 0 is not accepted (relative path) */ if (i != 4) { return; } gisdbase = std::string(tmp.get()); location = std::string(ptr[3]); mapset = std::string(ptr[2]); element = std::string(ptr[1]); name = std::string(ptr[0]); return; } auto GRASSRasterPath::isValid() -> bool { if (name.empty() || (element != "cellhd" && element != "group")) { return false; } return true; } auto GRASSRasterPath::isCellHD() -> bool { return element == "cellhd"; } /************************************************************************/ /* GDALRegister_GRASS() */ /************************************************************************/ void GDALRegister_GRASS() { if (!GDAL_CHECK_VERSION("GDAL/GRASS driver")) return; if (GDALGetDriverByName("GRASS") != nullptr) return; auto poDriver = new GDALDriver(); poDriver->SetDescription("GRASS"); poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GRASS Rasters (7+)"); poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/grass.html"); poDriver->pfnOpen = GRASSDataset::Open; GetGDALDriverManager()->RegisterDriver(poDriver); } gdal-grass-2.0.0/source/ogrgrass.h000066400000000000000000000135721510331751700170500ustar00rootroot00000000000000/****************************************************************************** * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Private definitions for OGR/GRASS driver. * Author: Radim Blazek, radim.blazek@gmail.com * ****************************************************************************** * Copyright (c) 2005, Radim Blazek * * SPDX-License-Identifier: MIT * ****************************************************************************/ #ifndef OGRGRASS_H_INCLUDED #define OGRGRASS_H_INCLUDED #include "gdal_version.h" #include "ogrsf_frmts.h" extern "C" { #include #include #include #include #include } /************************************************************************/ /* OGRGRASSLayer */ /************************************************************************/ class OGRGRASSLayer final : public OGRLayer { public: OGRGRASSLayer(int layer, struct Map_info *map); virtual ~OGRGRASSLayer(); // Layer info #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto GetName() const -> const char * override #else auto GetName() -> const char * override #endif { return osName.c_str(); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto GetLayerDefn() const -> const OGRFeatureDefn * override #else auto GetLayerDefn() -> OGRFeatureDefn * override #endif { return poFeatureDefn; } auto GetFeatureCount(int) -> GIntBig override; #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) auto IGetExtent(int iGeomField, OGREnvelope *psExtent, bool bForce) -> OGRErr override; #else auto GetExtent(OGREnvelope *psExtent, int bForce) -> OGRErr override; virtual auto GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) -> OGRErr override { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); } #endif #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto GetSpatialRef() const -> const OGRSpatialReference * override; auto TestCapability(const char *) const -> int override; #else auto GetSpatialRef() -> OGRSpatialReference * override; auto TestCapability(const char *) -> int override; #endif // Reading void ResetReading() override; virtual auto SetNextByIndex(GIntBig nIndex) -> OGRErr override; auto GetNextFeature() -> OGRFeature * override; auto GetFeature(GIntBig nFeatureId) -> OGRFeature * override; // Filters virtual auto SetAttributeFilter(const char *query) -> OGRErr override; #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) virtual OGRErr ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom) override; #else virtual void SetSpatialFilter(OGRGeometry *poGeomIn) override; virtual void SetSpatialFilter(int iGeomField, OGRGeometry *poGeom) override { OGRLayer::SetSpatialFilter(iGeomField, poGeom); } #endif private: std::string osName; OGRSpatialReference *poSRS; OGRFeatureDefn *poFeatureDefn; char *pszQuery; // Attribute filter string GIntBig iNextId; int nTotalCount; int iLayer; // Layer number int iLayerIndex; // Layer index (in GRASS category index) int iCatField; // Field where category (key) is stored int nFields; int *paFeatureIndex; // Array of indexes to category index array // Vector map struct Map_info *poMap; struct field_info *poLink; // Database connection bool bHaveAttributes; dbString *poDbString; dbDriver *poDriver; dbCursor *poCursor; bool bCursorOpened; // Sequential database cursor opened int iCurrentCat; // Current category in select cursor struct line_pnts *poPoints; struct line_cats *poCats; auto StartDbDriver() -> bool; auto StopDbDriver() -> bool; auto GetFeatureGeometry(long nFeatureId, int *cat) -> OGRGeometry *; auto SetAttributes(OGRFeature *feature, dbTable *table) -> bool; // Features matching spatial filter for ALL features/elements in GRASS char *paSpatialMatch; auto SetSpatialMatch() -> bool; // Features matching attribute filter for ALL features/elements in GRASS char *paQueryMatch; auto OpenSequentialCursor() -> bool; auto ResetSequentialCursor() -> bool; auto SetQueryMatch() -> bool; }; /************************************************************************/ /* OGRGRASSDataSource */ /************************************************************************/ class OGRGRASSDataSource final : public OGRDataSource { public: OGRGRASSDataSource() = default; virtual ~OGRGRASSDataSource(); auto Open(const char *, bool bUpdate, bool bTestOpen, bool bSingleNewFile = false) -> bool; auto GetName() -> const char * override { return osName.c_str(); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto GetLayerCount() const -> int override #else auto GetLayerCount() -> int override #endif { return nLayers; } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto GetLayer(int) const -> const OGRLayer * override; auto TestCapability(const char *) const -> int override; #else auto GetLayer(int) -> OGRLayer * override; auto TestCapability(const char *) -> int override; #endif private: OGRGRASSLayer **papoLayers{nullptr}; std::string osName; // Date source name std::string osGisdbase; // GISBASE std::string osLocation; // location name std::string osMapset; // mapset name std::string osMap; // name of vector map struct Map_info map { }; int nLayers{0}; bool bOpened{false}; auto SetPath(const char *) -> bool; }; #endif /* ndef OGRGRASS_H_INCLUDED */ gdal-grass-2.0.0/source/ogrgrassdatasource.cpp000066400000000000000000000226351510331751700214560ustar00rootroot00000000000000/****************************************************************************** * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements OGRGRASSDataSource class. * Author: Radim Blazek, radim.blazek@gmail.com * ****************************************************************************** * Copyright (c) 2005, Radim Blazek * Copyright (c) 2008-2020, Even Rouault * * SPDX-License-Identifier: MIT * ****************************************************************************/ #include #include #include "ogrgrass.h" #include "cpl_conv.h" #include "cpl_string.h" /************************************************************************/ /* Grass2CPLErrorHook() */ /************************************************************************/ static auto Grass2CPLErrorHook(char *pszMessage, int bFatal) -> int { if (!bFatal) CPLError(CE_Warning, CPLE_AppDefined, "GRASS warning: %s", pszMessage); else CPLError(CE_Warning, CPLE_AppDefined, "GRASS fatal error: %s", pszMessage); return 0; } /************************************************************************/ /* ~OGRGRASSDataSource() */ /************************************************************************/ OGRGRASSDataSource::~OGRGRASSDataSource() { for (int i = 0; i < nLayers; i++) delete papoLayers[i]; if (bOpened) Vect_close(&map); } /************************************************************************/ /* Open() */ /************************************************************************/ using GrassErrorHandler = auto(*)(const char *, int) -> int; auto OGRGRASSDataSource::Open(const char *pszNewName, bool /*bUpdate*/, bool bTestOpen, bool /*bSingleNewFileIn*/) -> bool { VSIStatBuf stat; CPLAssert(nLayers == 0); /* -------------------------------------------------------------------- */ /* Do the given path contains 'vector' and 'head'? */ /* -------------------------------------------------------------------- */ if (std::strstr(pszNewName, "vector") == nullptr || std::strstr(pszNewName, "head") == nullptr) { if (!bTestOpen) { CPLError(CE_Failure, CPLE_AppDefined, "%s is not GRASS vector, access failed.\n", pszNewName); } return false; } /* -------------------------------------------------------------------- */ /* Is the given a regular file? */ /* -------------------------------------------------------------------- */ if (CPLStat(pszNewName, &stat) != 0 || !VSI_ISREG(stat.st_mode)) { if (!bTestOpen) { CPLError(CE_Failure, CPLE_AppDefined, "%s is not GRASS vector, access failed.\n", pszNewName); } return false; } /* -------------------------------------------------------------------- */ /* Parse datasource name */ /* -------------------------------------------------------------------- */ if (!SetPath(pszNewName)) { if (!bTestOpen) { CPLError(CE_Failure, CPLE_AppDefined, "%s is not GRASS datasource name, access failed.\n", pszNewName); } return false; } CPLDebug("GRASS", "Gisdbase: %s", osGisdbase.c_str()); CPLDebug("GRASS", "Location: %s", osLocation.c_str()); CPLDebug("GRASS", "Mapset: %s", osMapset.c_str()); CPLDebug("GRASS", "Map: %s", osMap.c_str()); /* -------------------------------------------------------------------- */ /* Init GRASS library */ /* -------------------------------------------------------------------- */ // GISBASE is path to the directory where GRASS is installed, // it is necessary because there are database drivers. if (!getenv("GISBASE")) { static char *gisbaseEnv = nullptr; const char *gisbase = GRASS_GISBASE; CPLError(CE_Warning, CPLE_AppDefined, "GRASS warning: GISBASE " "environment variable was not set, using:\n%s", gisbase); std::array buf{}; (void)snprintf(buf.data(), buf.size(), "GISBASE=%s", gisbase); CPLFree(gisbaseEnv); gisbaseEnv = CPLStrdup(buf.data()); putenv(gisbaseEnv); } // Don't use GISRC file and read/write GRASS variables // (from location G_VAR_GISRC) to memory only. G_set_gisrc_mode(G_GISRC_MODE_MEMORY); // Init GRASS libraries (required). G_no_gisinit() doesn't check // write permissions for mapset compare to G_gisinit() G_no_gisinit(); // Set error function G_set_error_routine(GrassErrorHandler(Grass2CPLErrorHook)); /* -------------------------------------------------------------------- */ /* Set GRASS variables */ /* -------------------------------------------------------------------- */ G_setenv_nogisrc("GISDBASE", osGisdbase.c_str()); G_setenv_nogisrc("LOCATION_NAME", osLocation.c_str()); G_setenv_nogisrc("MAPSET", osMapset.c_str()); G_reset_mapsets(); G_add_mapset_to_search_path(osMapset.c_str()); /* -------------------------------------------------------------------- */ /* Open GRASS vector map */ /* -------------------------------------------------------------------- */ Vect_set_open_level(2); int level = Vect_open_old(&map, osMap.c_str(), osMapset.c_str()); if (level < 2) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot open GRASS vector %s on level 2.\n", osName.c_str()); return false; } CPLDebug("GRASS", "Num lines = %d", Vect_get_num_lines(&map)); /* -------------------------------------------------------------------- */ /* Build a list of layers. */ /* -------------------------------------------------------------------- */ int ncidx = Vect_cidx_get_num_fields(&map); CPLDebug("GRASS", "Num layers = %d", ncidx); for (int i = 0; i < ncidx; i++) { // Create the layer object auto poLayer = new OGRGRASSLayer(i, &map); // Add layer to data source layer list papoLayers = reinterpret_cast( CPLRealloc(papoLayers, sizeof(OGRGRASSLayer *) * (nLayers + 1))); papoLayers[nLayers++] = poLayer; } bOpened = true; return true; } /************************************************************************/ /* TestCapability() */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto OGRGRASSDataSource::TestCapability(const char * /* pszCap*/) const -> int #else auto OGRGRASSDataSource::TestCapability(const char * /* pszCap*/) -> int #endif { return FALSE; } /************************************************************************/ /* GetLayer() */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto OGRGRASSDataSource::GetLayer(int iLayer) const -> const OGRLayer * #else auto OGRGRASSDataSource::GetLayer(int iLayer) -> OGRLayer * #endif { if (iLayer < 0 || iLayer >= nLayers) return nullptr; else return papoLayers[iLayer]; } /************************************************************************/ /* SplitPath() */ /* Split full path to cell or group to: */ /* gisdbase, location, mapset, name */ /* New string are allocated and should be freed when no longer needed. */ /* */ /* Returns: true - OK */ /* false - failed */ /************************************************************************/ auto OGRGRASSDataSource::SetPath(const char *path) -> bool { CPLDebug("GRASS", "OGRGRASSDataSource::SetPath"); if (!path || std::strlen(path) == 0) return false; char *p = nullptr; std::array ptr{}; int i = 0; auto tmp = std::unique_ptr(new char[std::strlen(path) + 1]); std::strcpy(tmp.get(), path); while ((p = std::strrchr(tmp.get(), '/')) != nullptr && i < 5) { *p = '\0'; if (std::strlen(p + 1) == 0) /* repeated '/' */ continue; ptr[i++] = p + 1; } /* Note: empty GISDBASE == 0 is not accepted (relative path) */ if (i != 5) { return false; } if (std::strcmp(ptr[0], "head") != 0 || std::strcmp(ptr[2], "vector") != 0) { return false; } osName = std::string(path); osGisdbase = std::string(tmp.get()); osLocation = std::string(ptr[4]); osMapset = std::string(ptr[3]); osMap = std::string(ptr[1]); return true; } gdal-grass-2.0.0/source/ogrgrassdriver.cpp000066400000000000000000000036451510331751700206170ustar00rootroot00000000000000/****************************************************************************** * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements OGRGRASSDriver class. * Author: Radim Blazek, radim.blazek@gmail.com * ****************************************************************************** * Copyright (c) 2005, Radim Blazek * * SPDX-License-Identifier: MIT * ****************************************************************************/ #include "ogrgrass.h" #include "cpl_conv.h" #include "cpl_string.h" extern "C" { void RegisterOGRGRASS(); } /************************************************************************/ /* Open() */ /************************************************************************/ static auto GRASSDatasetOpen(GDALOpenInfo *poOpenInfo) -> GDALDataset * { auto *poDS = new OGRGRASSDataSource(); bool bUpdate = poOpenInfo->eAccess == GA_Update; if (!poDS->Open(poOpenInfo->pszFilename, bUpdate, true)) { delete poDS; return nullptr; } else { return poDS; } } /************************************************************************/ /* RegisterOGRGRASS() */ /************************************************************************/ void RegisterOGRGRASS() { if (!GDAL_CHECK_VERSION("OGR/GRASS driver")) return; if (GDALGetDriverByName("OGR_GRASS") != nullptr) return; auto *poDriver = new GDALDriver(); poDriver->SetDescription("OGR_GRASS"); poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "GRASS Vectors (5.7+)"); poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/grass.html"); poDriver->pfnOpen = GRASSDatasetOpen; GetGDALDriverManager()->RegisterDriver(poDriver); } gdal-grass-2.0.0/source/ogrgrasslayer.cpp000066400000000000000000001034141510331751700204330ustar00rootroot00000000000000/****************************************************************************** * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements OGRGRASSLayer class. * Author: Radim Blazek, radim.blazek@gmail.com * ****************************************************************************** * Copyright (c) 2005, Radim Blazek * Copyright (c) 2008-2020, Even Rouault * * SPDX-License-Identifier: MIT * ****************************************************************************/ #include #include #include "ogrgrass.h" #include "cpl_conv.h" /************************************************************************/ /* OGRGRASSLayer() */ /************************************************************************/ OGRGRASSLayer::OGRGRASSLayer(int layerIndex, struct Map_info *map) : poSRS(nullptr), pszQuery(nullptr), iNextId(0), iLayer(Vect_cidx_get_field_number(map, layerIndex)), iLayerIndex(layerIndex), poMap(map), poLink(Vect_get_field(poMap, iLayer)), iCurrentCat(0), poPoints(Vect_new_line_struct()), poCats(Vect_new_cats_struct()), paSpatialMatch(nullptr), paQueryMatch(nullptr) { CPLDebug("GRASS", "OGRGRASSLayer::OGRGRASSLayer layerIndex = %d", layerIndex); CPLDebug("GRASS", "iLayer = %d", iLayer); // poLink may be NULL if not defined // Layer name if (poLink && poLink->name) { osName = std::string(poLink->name); } else { osName = std::to_string(iLayer); } // Because we don't represent centroids as any simple feature, we have to scan // category index and create index of feature IDs pointing to category index nTotalCount = Vect_cidx_get_type_count(poMap, iLayer, GV_POINT | GV_LINES | GV_AREA); CPLDebug("GRASS", "nTotalCount = %d", nTotalCount); paFeatureIndex = static_cast(CPLMalloc(nTotalCount * sizeof(int))); int n = Vect_cidx_get_type_count(poMap, iLayer, GV_POINTS | GV_LINES | GV_AREA); int cnt = 0; for (int i = 0; i < n; i++) { int cat = 0, type = 0, id = 0; Vect_cidx_get_cat_by_index(poMap, iLayerIndex, i, &cat, &type, &id); if (!(type & (GV_POINT | GV_LINES | GV_AREA))) continue; paFeatureIndex[cnt++] = i; } poFeatureDefn = new OGRFeatureDefn(osName.c_str()); SetDescription(poFeatureDefn->GetName()); poFeatureDefn->Reference(); // Get type definition int nTypes = Vect_cidx_get_num_types_by_index(poMap, iLayerIndex); int types = 0; for (int i = 0; i < nTypes; i++) { int type = 0, count = 0; Vect_cidx_get_type_count_by_index(poMap, iLayerIndex, i, &type, &count); if (!(type & (GV_POINT | GV_LINES | GV_AREA))) continue; types |= type; CPLDebug("GRASS", "type = %d types = %d", type, types); } OGRwkbGeometryType eGeomType = wkbUnknown; if (types == GV_LINE || types == GV_BOUNDARY || types == GV_LINES) { eGeomType = wkbLineString; } else if (types == GV_POINT) { eGeomType = wkbPoint; } else if (types == GV_AREA) { CPLDebug("GRASS", "set wkbPolygon"); eGeomType = wkbPolygon; } if (Vect_is_3d(poMap)) poFeatureDefn->SetGeomType(wkbSetZ(eGeomType)); else poFeatureDefn->SetGeomType(eGeomType); // Get attributes definition poDbString = reinterpret_cast(CPLMalloc(sizeof(dbString))); poCursor = reinterpret_cast(CPLMalloc(sizeof(dbCursor))); bCursorOpened = FALSE; poDriver = nullptr; bHaveAttributes = false; db_init_string(poDbString); if (poLink) { if (StartDbDriver()) { db_set_string(poDbString, poLink->table); dbTable *table = nullptr; if (db_describe_table(poDriver, poDbString, &table) == DB_OK) { nFields = db_get_table_number_of_columns(table); iCatField = -1; for (int i = 0; i < nFields; i++) { dbColumn *column = db_get_table_column(table, i); int ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column)); OGRFieldType ogrFtype = OFTInteger; switch (ctype) { case DB_C_TYPE_INT: ogrFtype = OFTInteger; break; case DB_C_TYPE_DOUBLE: ogrFtype = OFTReal; break; case DB_C_TYPE_STRING: ogrFtype = OFTString; break; case DB_C_TYPE_DATETIME: ogrFtype = OFTDateTime; break; } CPLDebug("GRASS", "column = %s type = %d", db_get_column_name(column), ctype); OGRFieldDefn oField(db_get_column_name(column), ogrFtype); poFeatureDefn->AddFieldDefn(&oField); if (G_strcasecmp(db_get_column_name(column), poLink->key) == 0) { iCatField = i; } } if (iCatField >= 0) { bHaveAttributes = true; } else { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find key field"); db_close_database_shutdown_driver(poDriver); poDriver = nullptr; } } else { CPLError(CE_Failure, CPLE_AppDefined, "Cannot describe table %s", poLink->table); } db_close_database_shutdown_driver(poDriver); poDriver = nullptr; } } if (!bHaveAttributes && iLayer > 0) // Because features in layer 0 have no cats { OGRFieldDefn oField("cat", OFTInteger); poFeatureDefn->AddFieldDefn(&oField); } if (getenv("GISBASE")) // We have some projection info in GISBASE { // Note: we do not have to reset GISDBASE and LOCATION_NAME because // OGRGRASSLayer constructor is called from OGRGRASSDataSource::Open // where those variables are set struct Key_Value *projinfo = G_get_projinfo(); struct Key_Value *projunits = G_get_projunits(); char *srsWkt = GPJ_grass_to_wkt(projinfo, projunits, 0, 0); if (srsWkt) { poSRS = new OGRSpatialReference(srsWkt); poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); G_free(srsWkt); } G_free_key_value(projinfo); G_free_key_value(projunits); } } /************************************************************************/ /* ~OGRGRASSLayer() */ /************************************************************************/ OGRGRASSLayer::~OGRGRASSLayer() { if (bCursorOpened) { db_close_cursor(poCursor); } if (poDriver) { StopDbDriver(); } if (poFeatureDefn) poFeatureDefn->Release(); if (poSRS) poSRS->Release(); if (pszQuery) CPLFree(pszQuery); if (paFeatureIndex) CPLFree(paFeatureIndex); if (poLink) G_free(poLink); Vect_destroy_line_struct(poPoints); Vect_destroy_cats_struct(poCats); db_free_string(poDbString); CPLFree(poDbString); CPLFree(poCursor); if (paSpatialMatch) CPLFree(paSpatialMatch); if (paQueryMatch) CPLFree(paQueryMatch); } /************************************************************************/ /* StartDbDriver */ /************************************************************************/ auto OGRGRASSLayer::StartDbDriver() -> bool { CPLDebug("GRASS", "StartDbDriver()"); bCursorOpened = false; if (!poLink) { return false; } poDriver = db_start_driver_open_database(poLink->driver, poLink->database); if (poDriver == nullptr) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot open database %s by driver %s, " "check if GISBASE environment variable is set, the driver is " "available " " and the database is accessible.", poLink->driver, poLink->database); return false; } return true; } /************************************************************************/ /* StopDbDriver */ /************************************************************************/ auto OGRGRASSLayer::StopDbDriver() -> bool { if (!poDriver) { CPLError(CE_Failure, CPLE_AppDefined, "Driver is not started"); return true; // I think that true is OK here } // TODO!!!: Because of bug in GRASS library it is impossible // to stop drivers in FIFO order. Until this is fixed // we have to use kill CPLDebug("GRASS", "driver PID = %d", poDriver->pid); #if defined(_WIN32) || defined(__WIN32__) db_close_database_shutdown_driver(poDriver); #else if (kill(poDriver->pid, SIGINT) != 0) { if (kill(poDriver->pid, SIGKILL) != 0) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot stop database " "driver pid = %d", poDriver->pid); } } #endif bCursorOpened = false; return true; } /************************************************************************/ /* ResetReading() */ /************************************************************************/ void OGRGRASSLayer::ResetReading() { iNextId = 0; if (bCursorOpened) { ResetSequentialCursor(); } } /************************************************************************/ /* SetNextByIndex() */ /* */ /* If we already have an FID list, we can easily reposition */ /* ourselves in it. */ /************************************************************************/ auto OGRGRASSLayer::SetNextByIndex(GIntBig nIndex) -> OGRErr { if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr) { iNextId = 0; int count = 0; while (true) { if (iNextId >= nTotalCount) break; if (count == nIndex) break; // Attributes if (pszQuery != nullptr && !paQueryMatch[iNextId]) { iNextId++; continue; } // Spatial if (m_poFilterGeom && !paSpatialMatch[iNextId]) { iNextId++; continue; } count++; } } iNextId = (int)nIndex; return OGRERR_NONE; } /************************************************************************/ /* SetAttributeFilter */ /************************************************************************/ auto OGRGRASSLayer::SetAttributeFilter(const char *query) -> OGRErr { CPLDebug("GRASS", "SetAttributeFilter: %s", query); if (query == nullptr) { // Release old if any if (pszQuery) { CPLFree(pszQuery); pszQuery = nullptr; } if (paQueryMatch) { CPLFree(paQueryMatch); paQueryMatch = nullptr; } return OGRERR_NONE; } paQueryMatch = reinterpret_cast(CPLMalloc(nTotalCount)); memset(paQueryMatch, 0x0, nTotalCount); pszQuery = CPLStrdup(query); OGRLayer::SetAttributeFilter(query); // Otherwise crash on delete if (bHaveAttributes) { if (!poDriver) { StartDbDriver(); } if (poDriver) { if (bCursorOpened) { db_close_cursor(poCursor); bCursorOpened = false; } OpenSequentialCursor(); if (bCursorOpened) { SetQueryMatch(); db_close_cursor(poCursor); bCursorOpened = false; } else { CPLFree(pszQuery); pszQuery = nullptr; return OGRERR_FAILURE; } db_close_database_shutdown_driver(poDriver); poDriver = nullptr; } else { CPLFree(pszQuery); pszQuery = nullptr; return OGRERR_FAILURE; } } else { // Use OGR to evaluate category match for (int i = 0; i < nTotalCount; i++) { OGRFeature *feature = GetFeature(i); CPLDebug("GRASS", "i = %d eval = %d", i, m_poAttrQuery->Evaluate(feature)); if (m_poAttrQuery->Evaluate(feature)) { paQueryMatch[i] = 1; } } } return OGRERR_NONE; } /************************************************************************/ /* SetQueryMatch */ /************************************************************************/ auto OGRGRASSLayer::SetQueryMatch() -> bool { CPLDebug("GRASS", "SetQueryMatch"); // NOTE: we don't have to call ResetSequentialCursor() first because // this method is called immediately after OpenSequentialCursor() if (!bCursorOpened) { CPLError(CE_Failure, CPLE_AppDefined, "Cursor is not opened."); return false; } int more = 0; int cidx = 0; // index to category index int fidx = 0; // index to feature index (paFeatureIndex) // number of categories in category index int ncats = Vect_cidx_get_num_cats_by_index(poMap, iLayerIndex); dbTable *table = db_get_cursor_table(poCursor); while (true) { if (db_fetch(poCursor, DB_NEXT, &more) != DB_OK) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot fetch attributes."); return false; } if (!more) break; dbColumn *column = db_get_table_column(table, iCatField); dbValue *value = db_get_column_value(column); int cat = db_get_value_int(value); // NOTE: because of bug in GRASS library it is impossible to use // Vect_cidx_find_next // Go through category index until first record of current category // is found or a category > current is found int id = -1; while (cidx < ncats) { int cidxcat = 0; int type = 0; Vect_cidx_get_cat_by_index(poMap, iLayerIndex, cidx, &cidxcat, &type, &id); if (cidxcat < cat) { cidx++; continue; } if (cidxcat > cat) break; // Not found // We have the category we want, check type if (!(type & (GV_POINT | GV_LINES | GV_AREA))) { cidx++; continue; } // Both category and type match -> find feature and set it on while (true) { if (fidx > nTotalCount || paFeatureIndex[fidx] > cidx) { // should not happen break; } if (paFeatureIndex[fidx] == cidx) { paQueryMatch[fidx] = 1; fidx++; break; } fidx++; } cidx++; } if (id < 0) continue; // not found } return true; } /************************************************************************/ /* OpenSequentialCursor */ /************************************************************************/ auto OGRGRASSLayer::OpenSequentialCursor() -> bool { CPLDebug("GRASS", "OpenSequentialCursor: %s", pszQuery); if (!poDriver) { CPLError(CE_Failure, CPLE_AppDefined, "Driver not opened."); return false; } if (bCursorOpened) { db_close_cursor(poCursor); bCursorOpened = false; } std::array buf{}; (void)snprintf(buf.data(), buf.size(), "SELECT * FROM %s ", poLink->table); db_set_string(poDbString, buf.data()); if (pszQuery) { (void)snprintf(buf.data(), buf.size(), "WHERE %s ", pszQuery); db_append_string(poDbString, buf.data()); } (void)snprintf(buf.data(), buf.size(), "ORDER BY %s", poLink->key); db_append_string(poDbString, buf.data()); CPLDebug("GRASS", "Query: %s", db_get_string(poDbString)); if (db_open_select_cursor(poDriver, poDbString, poCursor, DB_SCROLL) == DB_OK) { iCurrentCat = -1; bCursorOpened = true; CPLDebug("GRASS", "num rows = %d", db_get_num_rows(poCursor)); } else { CPLError(CE_Failure, CPLE_AppDefined, "Cannot open cursor."); return false; } return true; } /************************************************************************/ /* ResetSequentialCursor */ /************************************************************************/ auto OGRGRASSLayer::ResetSequentialCursor() -> bool { CPLDebug("GRASS", "ResetSequentialCursor"); int more = 0; if (db_fetch(poCursor, DB_FIRST, &more) != DB_OK) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot reset cursor."); return false; } if (db_fetch(poCursor, DB_PREVIOUS, &more) != DB_OK) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot reset cursor."); return false; } return true; } /************************************************************************/ /* SetSpatialFilter */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) OGRErr OGRGRASSLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeomIn) #else void OGRGRASSLayer::SetSpatialFilter(OGRGeometry *poGeomIn) #endif { CPLDebug("GRASS", "SetSpatialFilter"); #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn); #else OGRLayer::SetSpatialFilter(poGeomIn); #endif if (poGeomIn == nullptr) { // Release old if any if (paSpatialMatch) { CPLFree(paSpatialMatch); paSpatialMatch = nullptr; } } else { SetSpatialMatch(); } #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) return OGRERR_NONE; #endif } /************************************************************************/ /* SetSpatialMatch */ /************************************************************************/ auto OGRGRASSLayer::SetSpatialMatch() -> bool { CPLDebug("GRASS", "SetSpatialMatch"); if (!paSpatialMatch) { paSpatialMatch = static_cast(CPLMalloc(nTotalCount)); } memset(paSpatialMatch, 0x0, nTotalCount); auto lstring = new OGRLineString(); lstring->setNumPoints(5); OGRGeometry *geom = lstring; for (int i = 0; i < nTotalCount; i++) { int cidx = paFeatureIndex[i]; int cat = 0, type = 0, id = 0; Vect_cidx_get_cat_by_index(poMap, iLayerIndex, cidx, &cat, &type, &id); struct bound_box box { }; switch (type) { case GV_POINT: case GV_LINE: case GV_BOUNDARY: Vect_get_line_box(poMap, id, &box); break; case GV_AREA: Vect_get_area_box(poMap, id, &box); break; } lstring->setPoint(0, box.W, box.N, 0.); lstring->setPoint(1, box.W, box.S, 0.); lstring->setPoint(2, box.E, box.S, 0.); lstring->setPoint(3, box.E, box.N, 0.); lstring->setPoint(4, box.W, box.N, 0.); if (FilterGeometry(geom)) { CPLDebug("GRASS", "Feature %d in filter", i); paSpatialMatch[i] = 1; } } delete lstring; return true; } /************************************************************************/ /* GetNextFeature() */ /************************************************************************/ auto OGRGRASSLayer::GetNextFeature() -> OGRFeature * { CPLDebug("GRASS", "OGRGRASSLayer::GetNextFeature"); OGRFeature *poFeature = nullptr; int cat = 0; // Get next iNextId while (true) { if (iNextId >= nTotalCount) // No more features { // Close cursor / driver if opened if (bCursorOpened) { db_close_cursor(poCursor); bCursorOpened = false; } if (poDriver) { db_close_database_shutdown_driver(poDriver); poDriver = nullptr; } return nullptr; } // Attributes if (pszQuery != nullptr && !paQueryMatch[iNextId]) { iNextId++; continue; } // Spatial if (m_poFilterGeom && !paSpatialMatch[iNextId]) { iNextId++; continue; } break; // Attributes & spatial filter match } OGRGeometry *poOGR = GetFeatureGeometry(iNextId, &cat); poFeature = new OGRFeature(poFeatureDefn); poFeature->SetGeometryDirectly(poOGR); poFeature->SetFID(iNextId); iNextId++; // Get attributes CPLDebug("GRASS", "bHaveAttributes = %d", bHaveAttributes); if (bHaveAttributes) { if (!poDriver) { StartDbDriver(); } if (poDriver) { if (!bCursorOpened) { OpenSequentialCursor(); } if (bCursorOpened) { dbTable *table = db_get_cursor_table(poCursor); if (iCurrentCat < cat) { while (true) { int more = 0; if (db_fetch(poCursor, DB_NEXT, &more) != DB_OK) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot fetch attributes."); break; } if (!more) break; dbColumn *column = db_get_table_column(table, iCatField); dbValue *value = db_get_column_value(column); iCurrentCat = db_get_value_int(value); if (iCurrentCat >= cat) break; } } if (cat == iCurrentCat) { SetAttributes(poFeature, table); } else { CPLError(CE_Failure, CPLE_AppDefined, "Attributes not found."); } } } } else if (iLayer > 0) // Add category { poFeature->SetField(0, cat); } m_nFeaturesRead++; return poFeature; } /************************************************************************/ /* GetFeature() */ /************************************************************************/ auto OGRGRASSLayer::GetFeature(GIntBig nFeatureId) -> OGRFeature * { CPLDebug("GRASS", "OGRGRASSLayer::GetFeature nFeatureId = " CPL_FRMT_GIB, nFeatureId); int cat = 0; OGRGeometry *poOGR = GetFeatureGeometry(nFeatureId, &cat); auto poFeature = new OGRFeature(poFeatureDefn); poFeature->SetGeometryDirectly(poOGR); poFeature->SetFID(nFeatureId); // Get attributes if (bHaveAttributes && !poDriver) { StartDbDriver(); } if (poDriver) { if (bCursorOpened) { db_close_cursor(poCursor); bCursorOpened = false; } CPLDebug("GRASS", "Open cursor for key = %d", cat); std::array buf{}; (void)snprintf(buf.data(), buf.size(), "SELECT * FROM %s WHERE %s = %d", poLink->table, poLink->key, cat); db_set_string(poDbString, buf.data()); if (db_open_select_cursor(poDriver, poDbString, poCursor, DB_SEQUENTIAL) == DB_OK) { iCurrentCat = cat; // Not important bCursorOpened = true; } else { CPLError(CE_Failure, CPLE_AppDefined, "Cannot open cursor."); } if (bCursorOpened) { int more = 0; if (db_fetch(poCursor, DB_NEXT, &more) != DB_OK) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot fetch attributes."); } else { if (!more) { CPLError(CE_Failure, CPLE_AppDefined, "Attributes not found."); } else { dbTable *table = db_get_cursor_table(poCursor); SetAttributes(poFeature, table); } } db_close_cursor(poCursor); bCursorOpened = false; } } else if (iLayer > 0) // Add category { poFeature->SetField(0, cat); } m_nFeaturesRead++; return poFeature; } /************************************************************************/ /* GetFeatureGeometry() */ /************************************************************************/ auto OGRGRASSLayer::GetFeatureGeometry(long nFeatureId, int *cat) -> OGRGeometry * { CPLDebug("GRASS", "OGRGRASSLayer::GetFeatureGeometry nFeatureId = %ld", nFeatureId); int cidx = paFeatureIndex[(int)nFeatureId]; int type = 0, id = 0; Vect_cidx_get_cat_by_index(poMap, iLayerIndex, cidx, cat, &type, &id); //CPLDebug ( "GRASS", "cat = %d type = %d id = %d", *cat, type, id ); OGRGeometry *poOGR = nullptr; int bIs3D = Vect_is_3d(poMap); switch (type) { case GV_POINT: { Vect_read_line(poMap, poPoints, poCats, id); if (bIs3D) poOGR = new OGRPoint(poPoints->x[0], poPoints->y[0], poPoints->z[0]); else poOGR = new OGRPoint(poPoints->x[0], poPoints->y[0]); } break; case GV_LINE: case GV_BOUNDARY: { Vect_read_line(poMap, poPoints, poCats, id); auto poOGRLine = new OGRLineString(); if (bIs3D) poOGRLine->setPoints(poPoints->n_points, poPoints->x, poPoints->y, poPoints->z); else poOGRLine->setPoints(poPoints->n_points, poPoints->x, poPoints->y); poOGR = poOGRLine; } break; case GV_AREA: { Vect_get_area_points(poMap, id, poPoints); auto poOGRPoly = new OGRPolygon(); auto poRing = new OGRLinearRing(); if (bIs3D) poRing->setPoints(poPoints->n_points, poPoints->x, poPoints->y, poPoints->z); else poRing->setPoints(poPoints->n_points, poPoints->x, poPoints->y); poOGRPoly->addRingDirectly(poRing); // Islands int nisles = Vect_get_area_num_isles(poMap, id); for (int i = 0; i < nisles; i++) { int isle = Vect_get_area_isle(poMap, id, i); Vect_get_isle_points(poMap, isle, poPoints); poRing = new OGRLinearRing(); if (bIs3D) poRing->setPoints(poPoints->n_points, poPoints->x, poPoints->y, poPoints->z); else poRing->setPoints(poPoints->n_points, poPoints->x, poPoints->y); poOGRPoly->addRingDirectly(poRing); } poOGR = poOGRPoly; } break; default: // Should not happen { CPLError(CE_Failure, CPLE_AppDefined, "Unknown GRASS feature type."); return nullptr; } } return poOGR; } /************************************************************************/ /* SetAttributes() */ /************************************************************************/ auto OGRGRASSLayer::SetAttributes(OGRFeature *poFeature, dbTable *table) -> bool { CPLDebug("GRASS", "OGRGRASSLayer::SetAttributes"); for (int i = 0; i < nFields; i++) { dbColumn *column = db_get_table_column(table, i); dbValue *value = db_get_column_value(column); int ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column)); if (!db_test_value_isnull(value)) { switch (ctype) { case DB_C_TYPE_INT: poFeature->SetField(i, db_get_value_int(value)); break; case DB_C_TYPE_DOUBLE: poFeature->SetField(i, db_get_value_double(value)); break; case DB_C_TYPE_STRING: poFeature->SetField(i, db_get_value_string(value)); break; case DB_C_TYPE_DATETIME: db_convert_column_value_to_string(column, poDbString); poFeature->SetField(i, db_get_string(poDbString)); break; } } db_convert_column_value_to_string(column, poDbString); // CPLDebug ( "GRASS", "val = %s", db_get_string ( poDbString )); } return true; } /************************************************************************/ /* GetFeatureCount() */ /* */ /* If a spatial filter is in effect, we turn control over to */ /* the generic counter. Otherwise we return the total count. */ /* Eventually we should consider implementing a more efficient */ /* way of counting features matching a spatial query. */ /************************************************************************/ auto OGRGRASSLayer::GetFeatureCount(int bForce) -> GIntBig { if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr) return OGRLayer::GetFeatureCount(bForce); return nTotalCount; } /************************************************************************/ /* GetExtent() */ /* */ /* Fetch extent of the data currently stored in the dataset. */ /* The bForce flag has no effect on SHO files since that value */ /* is always in the header. */ /* */ /* Returns OGRERR_NONE/OGRRERR_FAILURE. */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 11, 0) auto OGRGRASSLayer::IGetExtent(int /*iGeomField */, OGREnvelope *psExtent, bool /*bForce*/) -> OGRErr #else auto OGRGRASSLayer::GetExtent(OGREnvelope *psExtent, int /*bForce*/) -> OGRErr #endif { struct bound_box box { }; Vect_get_map_box(poMap, &box); psExtent->MinX = box.W; psExtent->MinY = box.S; psExtent->MaxX = box.E; psExtent->MaxY = box.N; return OGRERR_NONE; } /************************************************************************/ /* TestCapability() */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto OGRGRASSLayer::TestCapability(const char *pszCap) const -> int #else auto OGRGRASSLayer::TestCapability(const char *pszCap) -> int #endif { if (EQUAL(pszCap, OLCRandomRead)) return TRUE; else if (EQUAL(pszCap, OLCFastFeatureCount)) return TRUE; else if (EQUAL(pszCap, OLCFastSpatialFilter)) return FALSE; else if (EQUAL(pszCap, OLCFastGetExtent)) return TRUE; else if (EQUAL(pszCap, OLCFastSetNextByIndex)) return TRUE; else return FALSE; } /************************************************************************/ /* GetSpatialRef() */ /************************************************************************/ #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,12,0) auto OGRGRASSLayer::GetSpatialRef() const -> const OGRSpatialReference * #else auto OGRGRASSLayer::GetSpatialRef() -> OGRSpatialReference * #endif { return poSRS; } gdal-grass-2.0.0/utils/000077500000000000000000000000001510331751700147005ustar00rootroot00000000000000gdal-grass-2.0.0/utils/generate_release_notes.py000077500000000000000000000206611510331751700217640ustar00rootroot00000000000000#!/usr/bin/env python3 """Generate release notes using git log or GitHub API Needs PyYAML, Git, and GitHub CLI. """ import argparse import csv import json import re import subprocess import sys from collections import defaultdict from pathlib import Path import yaml PRETTY_TEMPLATE = ( " - hash: %H%n" " author_name: %aN%n" " author_email: %aE%n" " date: %ad%n" " message: |-%n %s" ) def remove_excluded_changes(changes, exclude): """Return a list of changes with excluded changes removed""" result = [] for change in changes: include = True for expression in exclude["regexp"]: if re.match(expression, change): include = False break if include: result.append(change) return result def round_down_to_five(value): """Round down to the nearest multiple of five""" base = 5 return value - (value % base) def split_to_categories(changes, categories): """Return dictionary of changes divided into categories *categories* is a list of dictionaries (mappings) with keys title and regexp. """ by_category = defaultdict(list) for change in changes: added = False for category in categories: if re.match(category["regexp"], change): by_category[category["title"]].append(change) added = True break if not added: by_category["Other Changes"].append(change) return by_category def print_section_heading_2(text, file=None): print(f"## {text}\n", file=file) def print_section_heading_3(text, file=None): print(f"### {text}\n", file=file) def print_category(category, changes, file=None): """Print changes for one category from dictionary of changes If *changes* don't contain a given category, nothing is printed. """ items = changes.get(category, None) if not items: return print_section_heading_3(category, file=file) for item in sorted(items): print(f"* {item}", file=file) print("") def print_by_category(changes, categories, file=None): """Print changes by categories from dictionary of changes""" for category in categories: print_category(category["title"], changes, file=file) print_category("Other Changes", changes, file=file) def print_notes( start_tag, end_tag, changes, categories, before=None, after=None, file=None ): """Print notes from given inputs *changes* is a list of strings. It will be sorted and ordered by category internally by this function. """ num_changes = round_down_to_five(len(changes)) print( f"The GDAL-GRASS driver {end_tag} release provides more than " f"{num_changes} improvements and fixes " f"with respect to the release {start_tag}.\n" ) if before: print(before) print_section_heading_2("What's Changed", file=file) changes_by_category = split_to_categories(changes, categories=categories) print_by_category(changes_by_category, categories=categories, file=file) if after: print(after) print("") def notes_from_gh_api(start_tag, end_tag, branch, categories, exclude): """Generate notes from GitHub API""" text = subprocess.run( [ "gh", "api", "repos/OSGeo/gdal-grass/releases/generate-notes", "-f", f"previous_tag_name={start_tag}", "-f", f"tag_name={end_tag}", "-f", f"target_commitish={branch}", ], capture_output=True, text=True, check=True, ).stdout body = json.loads(text)["body"] lines = body.splitlines() start_whats_changed = lines.index("## What's Changed") end_whats_changed = lines.index("", start_whats_changed) raw_changes = lines[start_whats_changed + 1 : end_whats_changed] changes = [] for change in raw_changes: if change.startswith("* ") or change.startswith("- "): changes.append(change[2:]) else: changes.append(change) changes = remove_excluded_changes(changes=changes, exclude=exclude) print_notes( start_tag=start_tag, end_tag=end_tag, changes=changes, before="\n".join(lines[:start_whats_changed]), after="\n".join(lines[end_whats_changed + 1 :]), categories=categories, ) def csv_to_dict(filename, key, value): """Read a CSV file as a dictionary""" result = {} with open(filename, encoding="utf-8", newline="") as csvfile: reader = csv.DictReader(csvfile) for row in reader: result[row[key]] = row[value] return result def notes_from_git_log(start_tag, end_tag, categories, exclude): """Generate notes from git log""" text = subprocess.run( ["git", "log", f"{start_tag}..{end_tag}", f"--pretty=format:{PRETTY_TEMPLATE}"], capture_output=True, text=True, check=True, ).stdout commits = yaml.safe_load(text) if not commits: raise RuntimeError("No commits retrieved from git log (try different tags)") config_directory = Path("utils") github_name_by_git_author = csv_to_dict( config_directory / "git_author_github_name.csv", key="git_author", value="github_name", ) lines = [] for commit in commits: if commit["author_email"].endswith("users.noreply.github.com"): github_name = commit["author_email"].split("@")[0] if "+" in github_name: github_name = github_name.split("+")[1] github_name = f"@{github_name}" else: # Emails are stored with @ replaced by a space. email = commit["author_email"].replace("@", " ") git_author = f"{commit['author_name']} <{email}>" if ( git_author in github_name_by_git_author ): github_name = github_name_by_git_author[git_author] github_name = f"@{github_name}" else: github_name = git_author lines.append(f"{commit['message']} by {github_name}") lines = remove_excluded_changes(changes=lines, exclude=exclude) print_notes( start_tag=start_tag, end_tag=end_tag, changes=lines, after=( "**Full Changelog**: " f"https://github.com/OSGeo/gdal-grass/compare/{start_tag}...{end_tag}" ), categories=categories, ) def create_release_notes(args): """Create release notes based on parsed command line parameters""" end_tag = args.end_tag if not end_tag: # git log has default, but the others do not. end_tag = subprocess.run( ["git", "rev-parse", "--verify", "HEAD"], capture_output=True, text=True, check=True, ).stdout.strip() config_directory = Path("utils") with open(config_directory / "release.yml", encoding="utf-8") as file: config = yaml.safe_load(file.read())["notes"] if args.backend == "api": notes_from_gh_api( start_tag=args.start_tag, end_tag=end_tag, branch=args.branch, categories=config["categories"], exclude=config["exclude"], ) else: notes_from_git_log( start_tag=args.start_tag, end_tag=end_tag, categories=config["categories"], exclude=config["exclude"], ) def main(): """Parse command line arguments and create release notes""" parser = argparse.ArgumentParser( description="Generate release notes from git log or GitHub API.", epilog="Run in utils directory to access the helper files.", ) parser.add_argument( "backend", choices=["log", "api"], help="use git log or GitHub API" ) parser.add_argument( "branch", help="needed for the GitHub API when tag does not exist" ) parser.add_argument("start_tag", help="old tag to compare against") parser.add_argument( "end_tag", help=( "new tag; " "if not created yet, " "an empty string for git log will use the current revision" ), ) args = parser.parse_args() try: create_release_notes(args) except subprocess.CalledProcessError as error: sys.exit(f"Subprocess '{' '.join(error.cmd)}' failed with: {error.stderr}") if __name__ == "__main__": main() gdal-grass-2.0.0/utils/git_author_github_name.csv000066400000000000000000000011741510331751700221270ustar00rootroot00000000000000git_author,github_name Alessandro Amici ,alexamici Radim Blazek ,blazek Huidae Cho ,HuidaeCho Bas Couwenberg ,sebastic Andrey Kiselev ,strezen Nicklas Larsson ,nilason Mateusz Loskot ,mloskot Markus Metz ,metzm Markus Neteler ,neteler Markus Neteler ,neteler Even Rouault ,rouault Even Rouault (bot) ,rouault-bot Frank Warmerdam ,warmerdam gdal-grass-2.0.0/utils/release.yml000066400000000000000000000015131510331751700170430ustar00rootroot00000000000000--- notes: categories: - title: Documentation and Messages regexp: '(docs?|man|manual|manual pages|[Ss]phinx|mkhtml|messages?|README): ' - title: Libraries and General Functionality regexp: '(grass_|lib|TGIS|tgis|raster|vector)[^ ]*: ' - title: Windows regexp: '(winGRASS|win|[Ww]indows): ' - title: Packaging, Configuration, Portability, and Compilation regexp: '(packaging|pkg|rpm|deb|pkg-config|configure|config|[Mm]ake|build|cmake): ' - title: Continuous Integration, Tests, Code Quality, and Checks regexp: '(CI|ci|CI\(deps\)|ci\(deps\)|[Tt]ests|[Cc]hecks|pytest): ' - title: Contributing and Management regexp: '(contributing|CONTRIBUTING.md|contributors|contributors.csv): ' exclude: regexp: - '[Hh]appy [Nn]ew [Yy]ear' - 'version: ' - 'RFC: '