├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── FEATURE_REQUEST.md │ └── bug-report.md ├── dependabot.yml ├── labeler.yml ├── release.yml └── workflows │ ├── auto-labeler.yml │ ├── documentation.yml │ ├── linter.yml │ ├── packager.yml │ ├── releaser.yml │ └── tester.yml ├── .gitignore ├── .pre-commit-config.yaml ├── AUTHORS ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── codecov.yml ├── docs ├── conf.py ├── development │ ├── contributing.md │ ├── documentation.md │ ├── environment.md │ ├── history.md │ └── testing.md ├── index.md ├── presentations │ ├── 2017_FOSS4G-E │ │ ├── 20170718_QGIS_GMLAS_toolbox_groundwater_monitoring_BRGM_OSLANDIA.pptx │ │ ├── GML application schema made easy in GDAL_OGR and QGIS - GMLAS driver.pdf │ │ ├── GML workshop_intro_chris_sylvain.pptx │ │ └── ReadMe.txt │ ├── 2018_EGU │ │ └── Demo_1_2_0_rc2_EPOS_WP15_EGU_with_datagraph.mp4 │ └── 2018_INSPIRE_conference │ │ └── 1.2.0_video_INSPIRE_PPI_conf_2018.mp4 ├── static │ └── img │ │ ├── download-data.png │ │ ├── download-from-url.png │ │ ├── download-service-bad-url.png │ │ ├── download-service.png │ │ ├── install-plugin.png │ │ ├── ogr-mapping-long-table-names.png │ │ ├── ogr-options-RemoveUnusedLayers.png │ │ ├── ogr-options-xlink-RegistryResolution.png │ │ ├── overview-toolbar.png │ │ ├── overview.png │ │ ├── plugin_gmlas_menu.png │ │ ├── plugin_gmlas_settings_into_qgis.png │ │ ├── qgis-relations-discovery.png │ │ ├── qgis_macros_warning.png │ │ ├── qgis_macros_warning_option.png │ │ ├── read-custom-timeseries-view.png │ │ ├── read-custom-timeseries.png │ │ ├── read-db-gmlas-config.png │ │ ├── read-db-gmlas-extent.png │ │ ├── read-db-gmlas-otheroptions.png │ │ ├── read-db-gmlas-source.png │ │ ├── read-db-gmlas-target.png │ │ ├── read-db-gmlas.png │ │ ├── read-db-schema.png │ │ ├── read-xml-attributetable.png │ │ ├── read-xml-featureinfo.png │ │ ├── read-xml-featureinfoform.png │ │ ├── read-xml-layerprops.png │ │ ├── read-xml-resolver-obs-after-insert.png │ │ ├── read-xml-resolver-obs.png │ │ ├── read-xml-resolver.png │ │ ├── read-xml-xpath.png │ │ └── testing │ │ ├── 1.GMLAS_information_seed_displayed.PNG │ │ ├── 1.GMLAS_load_layer.PNG │ │ ├── 1.information_seed.PNG │ │ ├── 1.information_seed_displayed.PNG │ │ ├── 2.GMLAS_de_rereferencing_vocabulary_filled.PNG │ │ ├── 2.GMLAS_de_rereferencing_vocabulary_filled_and_displayed.PNG │ │ ├── 2.de_rereferencing_vocabulary.PNG │ │ ├── 2.de_rereferencing_vocabulary_filled.PNG │ │ ├── 3.GMLAS_Geology_log_filled.PNG │ │ ├── 3.GMLAS_Geology_log_filled_and_displayed.PNG │ │ ├── 3.GMLAS_Geology_log_load_layer.PNG │ │ ├── 3.Geology_log_viewer.PNG │ │ ├── 3.sos_observationData.PNG │ │ ├── 5.SOS_TimeSeries_filled.PNG │ │ ├── 5.SOS_TimeSeries_filled_and_displayed.PNG │ │ ├── 5.SOS_TimeSeries_load_layer.PNG │ │ ├── 5.Timeseries_viewer.PNG │ │ ├── 5_sos_GetObservationResponse.PNG │ │ ├── 6.GMLAS_GWML2_filled.PNG │ │ ├── 6.GMLAS_GWML2_filled_and_displayed.PNG │ │ ├── 6.GMLAS_HydroGeoUnit_load_layer.PNG │ │ └── 6.GWML2_ressource_added.PNG ├── testing │ ├── scenario_mode_gmlas_local.md │ ├── scenario_mode_xml_content_negociation.md │ └── scenario_mode_xml_local.md ├── usage │ ├── download.md │ ├── quickstart.md │ ├── read_custom.md │ ├── read_files.md │ ├── read_mode_db.md │ ├── read_mode_xml.md │ ├── settings.md │ └── write_from_db.md └── usecases │ ├── UC_codelist.md │ ├── UC_create_inspire_data.md │ ├── UC_overlap.md │ ├── UC_vbox.md │ ├── convert-samples.sh │ ├── db.sql │ ├── index.md │ └── ogr_relationships_to_fk.sql ├── gml_application_schema_toolbox ├── __about__.py ├── __init__.py ├── conf │ ├── gmlasconf-inspire.xml │ └── gmlasconf.xml ├── constants.py ├── core │ ├── __init__.py │ ├── gml_utils.py │ ├── gmlas_xpath.py │ ├── load_gml_as_xml.py │ ├── load_gmlas_in_qgis.py │ ├── proxy.py │ ├── qgis_urlopener.py │ └── xml_utils.py ├── creation_dialog.py ├── doc │ └── index.html ├── extlibs │ ├── __init__.py │ └── owslib_hacks.py ├── gui │ ├── __init__.py │ ├── bbox_widget.py │ ├── bounded_by_style.qml │ ├── custom_viewers.py │ ├── database_widget.py │ ├── export_gmlas_panel.py │ ├── gmlas_panel_mixin.py │ ├── import_gmlas_panel.py │ ├── load_wizard.py │ ├── load_wizard_wfs.py │ ├── load_wizard_xml.py │ ├── progress_bar.py │ ├── qgis_form_custom_widget.py │ ├── wait_cursor_context.py │ ├── xml_custom_widget.py │ ├── xml_dialog.py │ └── xml_tree_widget.py ├── main.py ├── metadata.txt ├── model_dialog.py ├── processing │ ├── __init__.py │ ├── gdal │ │ ├── __init__.py │ │ └── ogr2ogr_3_24.py │ └── provider.py ├── resources.qrc ├── resources │ ├── gui │ │ ├── __init__.py │ │ ├── dlg_settings.py │ │ └── dlg_settings.ui │ └── images │ │ ├── drill.svg │ │ ├── info-circle.svg │ │ ├── logo_brgm.svg │ │ ├── logo_c2c.svg │ │ ├── logo_eea.png │ │ ├── logo_oslandia.png │ │ ├── logo_spatialys.png │ │ ├── mActionAddGMLLayer.svg │ │ ├── mActionOpenTableGML.svg │ │ ├── mActionShowSchema.svg │ │ └── plot.svg ├── sql │ ├── add_foreign_key_constraint.sql │ ├── add_unique_constraint.sql │ ├── constraint_exists.sql │ ├── drop_constraint.sql │ ├── foreign_key_many_to_many.sql │ ├── foreign_key_one_to_many.sql │ ├── get_1_1_relations.sql │ └── get_1_n_relations.sql ├── toolbelt │ ├── __init__.py │ ├── file_downloader.py │ ├── log_handler.py │ ├── network_manager.py │ └── preferences.py ├── ui │ ├── bbox_widget.ui │ ├── database_widget.ui │ ├── export_gmlas_panel.ui │ ├── import_gmlas_panel.ui │ ├── load_wizard_data_source.ui │ ├── load_wizard_load.ui │ ├── load_wizard_wfs.ui │ ├── load_wizard_xml_options.ui │ └── xml_dialog.ui └── viewers │ ├── README.md │ ├── __init__.py │ ├── geology_logs.py │ ├── viewers_utils.py │ └── wml2_timeseries.py ├── requirements ├── development.txt ├── documentation.txt ├── packaging.txt └── testing.txt ├── setup.cfg └── tests ├── __init__.py ├── basic_test_scenario ├── 0_BoreholeView.xml ├── 1_RawGeologicLogs.xml ├── 3_GroundWater_Quantity_Monitoring_Facility.xml ├── 4_SOS_TimeSeries.xml └── 5_HydroGeoUnit.xml ├── dev └── docker-compose_postgis.yml ├── fixtures ├── BRGM_raw_database_observation_waterml2_output.xml ├── EUReg.example.xml ├── brgm_ef_piezo_50_2.xml ├── geology_log1.sqlite ├── gmlasconf.xml └── timeseries1.sqlite ├── qgis ├── __init__.py ├── test_custom_viewers.py ├── test_gmlas_xpath.py ├── test_load_as_xml.py ├── test_load_in_qgis.py └── test_plg_preferences.py ├── samples ├── AQD_UBA_Sample_50.xml ├── AQD_UBA_SamplingPoint_50.xml ├── AQD_UBA_Station_50.xml ├── BRGM_1GeolUnit_GeoSciML.xml ├── BRGM_1GeolUnit_INSPIRE.xml ├── BRGM_1MappedFeature_GeoSciML.xml ├── BRGM_1MappedFeature_INSPIRE.xml ├── BRGM_1MineralOccurence_INSPIRE.xml ├── BRGM_environmental_monitoring_facility_piezometer_1.xml ├── BRGM_environmental_monitoring_facility_piezometer_50.xml ├── BRGM_raw_database_observation_PointTimeseriesObservation_output.xml ├── BoreholeView.xml ├── BoreholeView_Instance.xml ├── GSML4-Borehole.xml ├── GW_AquiferSystem_BRGM-uc1.xml ├── GW_AquiferSystem_BoM_uc1-maxFeatures5.xml ├── GW_Spring_GNS_uc1.xml ├── GW_Well_BRGM-uc1.xml ├── bigSizeFiles │ └── ReadMe_testsFeedbacks_and_file_references.txt ├── href# │ ├── GWML2.xml │ └── OM_Observation.xml ├── liste_flux.txt └── swe │ ├── AQD │ ├── AQD_Envirocloud_oneOffering.xml │ ├── AQD_UBA_oneOffering.xml │ └── readMe_endpoints.txt │ └── GW_GeologyLog │ ├── BSS001REWW_Log.xml │ ├── GW_Well_GNS_uc1.xml │ ├── GW_Well_GSC_uc1.xml │ ├── GeologyLogCoverage_BSS_geologicage.xml │ ├── GeologyLogCoverage_BSS_lithology.xml │ └── GeologyLogCoverage_LogISO_stratigraphicunit.xml ├── tests_qgis.dockerfile └── unit ├── __init__.py └── test_plg_metadata.py /.dockerignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | **/.classpath 3 | **/.dockerignore 4 | **/.env 5 | **/.git 6 | **/.github 7 | **/.gitignore 8 | **/.project 9 | **/.pytest_cache 10 | **/.settings 11 | **/.toolstarget 12 | **/.venv 13 | **/.vs 14 | **/.vscode 15 | **/*.*proj.user 16 | **/*.dbmdl 17 | **/*.jfm 18 | **/azds.yaml 19 | **/bin 20 | **/charts 21 | **/htmlcov 22 | **/docker-compose* 23 | **/Dockerfile* 24 | **/junit 25 | **/node_modules 26 | **/npm-debug.log 27 | **/obj 28 | **/secrets.dev.yaml 29 | **/site 30 | **/values.dev.yaml 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for a new feature or an improvement 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Is your feature request related to a problem? Please describe 11 | 12 | 13 | # Describe the solution you'd like 14 | 15 | 16 | # Describe alternatives you've considered 17 | 18 | 19 | # Additional context 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report errors and problems to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Environment 11 | 12 | 13 | 14 | - Plugin version: 15 | - QGIS version: 16 | - Operating System: 17 | 18 | # Description 19 | 20 | <-- Here write a clear and concise description of what the bug is. --> 21 | 22 | ## To Reproduce 23 | 24 | Steps to reproduce the behavior: 25 | 26 | 1. Go to '...' 27 | 2. Click on '....' 28 | 3. Scroll down to '....' 29 | 4. See error 30 | 31 | ## Expected behavior 32 | 33 | <-- A clear and concise description of what you expected to happen. --> 34 | 35 | ---- 36 | 37 | # Additional context 38 | 39 | <-- Add any other context about the problem here. --> 40 | 41 | ## Logs 42 | 43 | ```logs 44 | Include some logs here, in QGIS Logs panel, bottom right icon, the small bubbles. 45 | ``` 46 | 47 | ## Screenshots 48 | 49 | <-- If applicable, add screenshots to help explain your problem. --> 50 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/requirements" 5 | schedule: 6 | interval: monthly 7 | time: "04:00" 8 | labels: 9 | - dependencies 10 | 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "monthly" 15 | labels: 16 | - ci-cd 17 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | ci-cd: 2 | - .github/**/*.yml 3 | 4 | dependencies: 5 | - ./**/requirements.txt 6 | - requirements.txt 7 | 8 | documentation: 9 | - docs/**/* 10 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - dependabot 5 | - pre-commit-ci 6 | categories: 7 | - title: Bugs fixes 🐛 8 | labels: 9 | - bug 10 | - title: Features and enhancements 🎉 11 | labels: 12 | - enhancement 13 | - GUI 14 | - Plugin logic 15 | - title: Tooling 🔧 16 | labels: 17 | - ci-cd 18 | - title: Documentation 📖 19 | labels: 20 | - documentation 21 | - title: Other Changes 22 | labels: 23 | - "*" 24 | -------------------------------------------------------------------------------- /.github/workflows/auto-labeler.yml: -------------------------------------------------------------------------------- 1 | name: "🏷 PR Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | jobs: 6 | triage: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/labeler@v4 10 | with: 11 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 12 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: "📚 Documentation" 2 | 3 | env: 4 | PROJECT_FOLDER: "gml_application_schema_toolbox" 5 | PYTHON_VERSION: 3.7 6 | 7 | on: 8 | push: 9 | branches: [master] 10 | paths: 11 | - "docs/**/*" 12 | - ".github/workflows/doc2pages.yml" 13 | 14 | jobs: 15 | doc-publisher: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | 22 | - name: Setup Python 23 | uses: actions/setup-python@v4.2.0 24 | with: 25 | python-version: ${{ env.PYTHON_VERSION }} 26 | 27 | - uses: actions/cache@v3 28 | with: 29 | path: ~/.cache/pip 30 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements/documentation.txt') }} 31 | restore-keys: | 32 | ${{ runner.os }}-pip- 33 | 34 | - name: Install project requirements 35 | run: | 36 | python -m pip install -U pip setuptools wheel 37 | python -m pip install -U -r requirements/documentation.txt 38 | 39 | - name: Build doc using Sphinx 40 | run: sphinx-build -b html docs docs/_build/html 41 | 42 | - uses: actions/upload-artifact@v3 43 | with: 44 | name: ${{ env.PROJECT_FOLDER }}-doc-latest 45 | path: docs/_build/html 46 | if-no-files-found: error 47 | 48 | - name: Deploy to GitHub Pages 49 | run: ghp-import --force --no-history --no-jekyll --push docs/_build/html 50 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | name: "✅ Linter" 2 | 3 | env: 4 | PROJECT_FOLDER: "gml_application_schema_toolbox" 5 | PYTHON_VERSION: 3.7 6 | 7 | on: 8 | push: 9 | branches: [master] 10 | paths: 11 | - "**.py" 12 | pull_request: 13 | branches: [master] 14 | paths: 15 | - "**.py" 16 | 17 | jobs: 18 | lint-py: 19 | name: Python 🐍 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Setup Python 27 | uses: actions/setup-python@v4.2.0 28 | with: 29 | python-version: ${{ env.PYTHON_VERSION }} 30 | 31 | - uses: actions/cache@v3 32 | with: 33 | path: ~/.cache/pip 34 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements/*.txt') }} 35 | restore-keys: | 36 | ${{ runner.os }}-pip- 37 | 38 | - name: Install project requirements 39 | run: | 40 | python -m pip install -U pip setuptools wheel 41 | python -m pip install -U flake8 42 | 43 | - name: Lint with flake8 44 | run: | 45 | # stop the build if there are Python syntax errors or undefined names 46 | flake8 ${{ env.PROJECT_FOLDER }} --count --select=E9,F63,F7,F82 --show-source --statistics 47 | # exit-zero treats all errors as warnings. 48 | flake8 ${{ env.PROJECT_FOLDER }} --count --exit-zero --statistics 49 | -------------------------------------------------------------------------------- /.github/workflows/packager.yml: -------------------------------------------------------------------------------- 1 | name: "📦 Packager" 2 | 3 | env: 4 | PROJECT_FOLDER: "gml_application_schema_toolbox" 5 | PYTHON_VERSION: 3.8 6 | 7 | on: 8 | push: 9 | branches: [master] 10 | 11 | jobs: 12 | packaging: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | 19 | - name: Setup Python 20 | uses: actions/setup-python@v4.2.0 21 | with: 22 | python-version: ${{ env.PYTHON_VERSION }} 23 | 24 | - name: Cache dependencie 25 | uses: actions/cache@v3 26 | with: 27 | path: ~/.cache/pip 28 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements/*.txt') }} 29 | restore-keys: | 30 | ${{ runner.os }}-pip- 31 | 32 | - name: Install project requirements 33 | run: | 34 | python -m pip install -U pip setuptools wheel 35 | python -m pip install -U -r requirements/packaging.txt 36 | 37 | - name: Package the latest version 38 | run: | 39 | qgis-plugin-ci package --allow-uncommitted-changes latest 40 | 41 | - uses: actions/upload-artifact@v3 42 | with: 43 | name: ${{ env.PROJECT_FOLDER }}-latest 44 | path: ${{ env.PROJECT_FOLDER }}.*.zip 45 | if-no-files-found: error 46 | -------------------------------------------------------------------------------- /.github/workflows/releaser.yml: -------------------------------------------------------------------------------- 1 | name: "🚀 Release" 2 | 3 | env: 4 | PROJECT_FOLDER: "gml_application_schema_toolbox" 5 | PYTHON_VERSION: 3.7 6 | 7 | on: 8 | push: 9 | tags: 10 | - "*" 11 | 12 | jobs: 13 | package: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Setup Python 20 | uses: actions/setup-python@v4.2.0 21 | with: 22 | python-version: ${{ env.PYTHON_VERSION }} 23 | cache: "pip" 24 | cache-dependency-path: "requirements/packaging.txt" 25 | 26 | - name: Get tag name as version 27 | id: get_version 28 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} 29 | 30 | - name: Install project requirements 31 | run: | 32 | python -m pip install -U pip setuptools wheel 33 | python -m pip install -U -r requirements/packaging.txt 34 | 35 | - name: Get current changelog for ${VERSION} 36 | run: qgis-plugin-ci changelog ${GITHUB_REF/refs\/tags\//} >> release.md 37 | 38 | - name: Create GitHub Release 39 | uses: softprops/action-gh-release@v1 40 | with: 41 | fail_on_unmatched_files: true 42 | bodyFile: release.md 43 | generate_release_notes: true 44 | 45 | - name: Deploy plugin 46 | run: >- 47 | qgis-plugin-ci 48 | release ${GITHUB_REF/refs\/tags\//} 49 | --allow-uncommitted-changes 50 | --github-token ${{ secrets.GITHUB_TOKEN }} 51 | --osgeo-username ${{ secrets.OSGEO_USER }} 52 | --osgeo-password ${{ secrets.OSGEO_PASSWORD }} 53 | -------------------------------------------------------------------------------- /.github/workflows/tester.yml: -------------------------------------------------------------------------------- 1 | name: "🎳 Tester" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - "**.py" 8 | 9 | pull_request: 10 | branches: [master] 11 | paths: 12 | - "**.py" 13 | 14 | env: 15 | PROJECT_FOLDER: "gml_application_schema_toolbox" 16 | # PYTEST_DEBUG: false 17 | PYTHON_VERSION: 3.7 18 | 19 | jobs: 20 | tests-unit: 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Get source code 25 | uses: actions/checkout@v3 26 | 27 | - name: Set up Python 28 | uses: actions/setup-python@v4.2.0 29 | with: 30 | python-version: ${{ env.PYTHON_VERSION }} 31 | 32 | - uses: actions/cache@v3 33 | with: 34 | path: ~/.cache/pip 35 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements/*.txt') }} 36 | restore-keys: | 37 | ${{ runner.os }}-pip- 38 | 39 | - name: Install Python requirements 40 | run: | 41 | python -m pip install -U pip setuptools wheel 42 | python -m pip install -U -r requirements/testing.txt 43 | 44 | - name: Run Unit tests 45 | run: pytest tests/unit/ 46 | 47 | - name: Upload coverage to Codecov 48 | uses: codecov/codecov-action@v3 49 | 50 | test-qgis: 51 | runs-on: ubuntu-20.04 52 | 53 | container: 54 | image: qgis/qgis:release-3_16 55 | env: 56 | DISPLAY: ":99" 57 | options: -v ${{ github.workspace }}:/tests_directory 58 | 59 | steps: 60 | - name: Get source code 61 | uses: actions/checkout@v3 62 | 63 | - name: Print QGIS version 64 | run: qgis --version 65 | 66 | - uses: actions/cache@v3 67 | with: 68 | path: ~/.cache/pip 69 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements/*.txt') }} 70 | restore-keys: | 71 | ${{ runner.os }}-pip- 72 | 73 | - name: Install Python requirements 74 | run: | 75 | python3 -m pip install -U pip setuptools wheel 76 | python3 -m pip install -U -r requirements/testing.txt 77 | 78 | - name: Run Unit tests 79 | run: pytest tests/qgis/ 80 | 81 | - name: Upload coverage to Codecov 82 | uses: codecov/codecov-action@v3 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # python 2 | __pycache__ 3 | *.pyc 4 | 5 | # virtual environment 6 | venv 7 | .venv 8 | 9 | # tests 10 | junit/ 11 | .coverage 12 | coverage.xml 13 | htmlcov/ 14 | 15 | # tooling 16 | *.log 17 | 18 | # docs 19 | docs/_apidoc/ 20 | docs/_build/ 21 | 22 | # qt stuff 23 | *_rc.py 24 | 25 | # project 26 | *~ 27 | *.bak 28 | *.qgs 29 | *.qgz 30 | *.zip 31 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: "node_modules|migrations|.venv|tests/dev/|tests/fixtures/|presentations|samples" 2 | fail_fast: false 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v5.0.0 6 | hooks: 7 | - id: check-added-large-files 8 | args: ["--maxkb=500"] 9 | - id: check-case-conflict 10 | - id: check-xml 11 | - id: check-yaml 12 | - id: detect-private-key 13 | - id: end-of-file-fixer 14 | - id: fix-byte-order-marker 15 | - id: fix-encoding-pragma 16 | args: [--remove] 17 | - id: trailing-whitespace 18 | args: [--markdown-linebreak-ext=md] 19 | 20 | - repo: https://github.com/psf/black 21 | rev: 24.10.0 22 | hooks: 23 | - id: black 24 | 25 | - repo: https://github.com/pycqa/isort 26 | rev: 5.13.2 27 | hooks: 28 | - id: isort 29 | args: ["--profile", "black", "--filter-files"] 30 | 31 | - repo: https://github.com/pycqa/flake8 32 | rev: 7.1.1 33 | hooks: 34 | - id: flake8 35 | language: python 36 | files: ^gml_application_schema_toolbox/.*\.py$ 37 | types: [python] 38 | additional_dependencies: ["flake8-qgis<2"] 39 | args: 40 | [ 41 | "--config=setup.cfg", 42 | "--select=E9,F63,F7,F82,QGS101,QGS102,QGS103,QGS104,QGS106", 43 | ] 44 | 45 | ci: 46 | autofix_prs: true 47 | autoupdate_schedule: quarterly 48 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Arnaud Morvan / Camptocamp 2 | Christian Ansorge / EEA 3 | Even Rouault / Spatialys 4 | Francois Prunayre / titellus 5 | Hugo Mercier / Oslandia 6 | Julien Moura / Oslandia 7 | Mickael Beaufils / BRGM 8 | Paul Hasenohr / JRC 9 | Patrick Valsecchi / Camptocamp 10 | Sylvain Grellet / BRGM 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.4.0-beta6 - 2022-09-23 4 | 5 | - Rework database selection: fix for spatialite selection #237 by @towa 6 | 7 | ## 1.4.0-beta5 - 2022-09-19 8 | 9 | - Bug fixes, mainly by @towa (Thanks!) 10 | 11 | ## 1.4.0-beta4 - 13/01/2022 12 | 13 | - fix some issues with the new database widget 14 | - fix debug mode 15 | - restore buttons related to foreign key management 16 | - use a processing to convert GMLAS file to database and vice versa instead of subprocessed calls to GDAL 17 | - dependencies upgrade 18 | 19 | ## 1.4.0-beta3 - 13/08/2021 20 | 21 | - fix URI error #163 22 | - modernize database widget 23 | - improved tests 24 | 25 | ## 1.4.0-beta2 - 02/07/2021 26 | 27 | - fix settings and debug mode which was lost in git confusion 28 | - unit tests enlarged 29 | 30 | ## 1.4.0-beta1 - 21/06/2021 31 | 32 | - network requests use the better options from PyQGIS 33 | - settings windows has been redesigned and integrated into QGIS preferences 34 | - minimal QGIS version is now 3.16 35 | - unit tests have been restored 36 | - fix some bugs 37 | - UI improvments (icons...) 38 | 39 | ## 1.3.1 - 08/03/2021 40 | 41 | ### Fixed 42 | 43 | - request headers were not transmitted in case of redirections [#127](https://github.com/BRGM/gml_application_schema_toolbox/issues/127) 44 | - remove a method which was overriding Python builtin functions [#129](https://github.com/BRGM/gml_application_schema_toolbox/issues/129) 45 | - fix path in File Dialog [#130](https://github.com/BRGM/gml_application_schema_toolbox/issues/130) 46 | 47 | ## 1.3.0 - 03/03/2021 48 | 49 | - refactoring of network requests to better integration in QGIS 50 | - add log abilities 51 | - documentation completly overhauled with updated use cases, docstrings and publication workflow 52 | 53 | ## 1.3.0-beta1 - 18/12/2020 54 | 55 | > Tagged as 1.2.1-beta1 in QGIS Official plugins repository 56 | 57 | - refactoring of network requests to better integration in QGIS 58 | - add log abilities 59 | - documentation completly overhauled with updated use cases, docstrings and publication workflow 60 | - fix compatibility with QGIS >= 3.13 ([#94](https://github.com/BRGM/gml_application_schema_toolbox/issues/94)) 61 | - start using GitHub Actions for continuous integration and deployment 62 | - bump development guidelines using black, flake8, isort and precommit [#95](https://github.com/BRGM/gml_application_schema_toolbox/pull/95) 63 | - add template for issues [#97](https://github.com/BRGM/gml_application_schema_toolbox/pull/97) 64 | - bump codebase to Python 3 only [#98](https://github.com/BRGM/gml_application_schema_toolbox/pull/98) 65 | - automate release workflow using qgis-plugin-ci [#101](https://github.com/BRGM/gml_application_schema_toolbox/pull/101) 66 | 67 | ## 1.2.0 - 08/06/2018 68 | 69 | New major version, with the following main changes: 70 | 71 | - XML mode: support for multiple geometries 72 | - XML mode: support for polyhedral / curves 73 | - UI refactor using a Wizard 74 | - GMLAS mode: handle custom viewers 75 | - GMLAS mode: add href resolution 76 | - WFS version negotiation 77 | - Lots of bug fixes 78 | 79 | This work has been funded by [BRGM](http://www.brgm.fr) and [the Association of Finnish Local and Regional Authorities](https://www.localfinland.fi/) (via [Gispo](http://www.gispo.fi/)) 80 | 81 | ## 1.1.4 - 06/11/2017 82 | 83 | - Handle multi geometries (#30) 84 | - Allow nested FeatureCollection/members (#29) 85 | 86 | ## 1.1.3 - 17/07/2017 87 | 88 | - Fix download tab 89 | - Fix XML mode with non-spatiail layers 90 | 91 | ## 1.1.2 - 17/07/2017 92 | 93 | - Fix encoding issues 94 | - Code cleanup 95 | - Fix error during export 96 | - Fix handling of unsafe SSL 97 | 98 | ## 1.1.1 - 13/07/2017 99 | 100 | - Use ogr API instead of QGIS to load spatialite layers 101 | - Add TypingConstraints for wml2 and geologylog 102 | - Handle layers with multiple geometries 103 | 104 | ## 1.1.0 - 11/07/2017 105 | 106 | - Remove dependency to pyspatialite 107 | - Bring back XML mode with custom widgets 108 | - Add a custom widget for geology logs 109 | - GMLAS import: option to automatically load layers and configure relations + editor widgets 110 | 111 | ## 1.0.0 - 15/12/2016 112 | 113 | - Add WFS 2 download panel 114 | - Add OGR GMLAS convert panel 115 | - Remove DB relational mode using PyXB (replaced by GMLAS OGR mode) 116 | - Add OGR GMLAS write panel 117 | 118 | ## 0.8.4 - 17/08/2016 119 | 120 | - Handle HTTP 302 redirections 121 | 122 | ## 0.8.3 - 27/06/2016 123 | 124 | - Add licences 125 | - Add doc / readme 126 | 127 | ## 0.8.2 - 27/06/2016 128 | 129 | - Fix custom viewer SQL queries to include order by 130 | 131 | ## 0.8.1 - 24/06/2016 132 | 133 | - Fix an ownership bug only seen on Windows 134 | 135 | ## 0.8.0 - 20/06/2016 136 | 137 | - Add an 'enforce not null constraints' option 138 | - Treat SOS:observationData as features 139 | - Enable custom widgets for embedded href 140 | - Add viewer plugins to the relational mode 141 | - Embed the XML tree widget in the attribute form (and remove custom identify / attribute table buttons) 142 | 143 | ## 0.7.3 - 16/06/2016 144 | 145 | - Fix URI resolution under Windows 146 | 147 | ## 0.7.1 - 15/06/2016 148 | 149 | - Fix remote URI resolving 150 | - Fix XPath handling 151 | - Fix default SRID 152 | - Add support for a Date/Time type in xpath mappings 153 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | First off, thanks for considering to contribute to this project! 4 | 5 | These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 6 | 7 | ## Git hooks 8 | 9 | We use git hooks through [pre-commit](https://pre-commit.com/) to enforce and automatically check some "rules". Please install it before to push any commit. 10 | 11 | See the relevant configuration file: `.pre-commit-config.yaml`. 12 | 13 | ## Code Style 14 | 15 | Make sure your code *roughly* follows [PEP-8](https://www.python.org/dev/peps/pep-0008/) and keeps things consistent with the rest of the code: 16 | 17 | - docstrings: [sphinx-style](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html#the-sphinx-docstring-format) is used to write technical documentation. 18 | - formatting: [black](https://black.readthedocs.io/) is used to automatically format the code without debate. 19 | - static analisis: [flake8](https://flake8.pycqa.org/en/latest/) is used to catch some dizziness and keep the source code healthy. 20 | - static typing: following [PEP-484](https://www.python.org/dev/peps/pep-0484/) type annotations are used to improve code readability and reliability. 21 | 22 | ---- 23 | 24 | ## Git flow 25 | 26 | ### Branches naming pattern 27 | 28 | The pattern is: `{category}/{issue-code}_-_{slugified-description}`. Where: 29 | 30 | - `category` is the type of work. Can be: `feature`, `bug`, `tooling`, `refactor`, `test`, `chore`, `release`, `hotfix`, `docs`, `ci`, `deploy` or `release-candidate`. 31 | - `issue-code` is the GitHub issue number followed by an underscore. If it's not relevant, ignore this. 32 | - `slugified-description` is the description of the work, slugified. 33 | 34 | Example: `feature/137_content_negociation` 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GML Application Schema toolbox QGIS plugin 2 | 3 | [![documentation badge](https://img.shields.io/badge/documentation-autobuilt%20with%20Sphinx-blue)](https://brgm.github.io/gml_application_schema_toolbox/) 4 | 5 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 6 | [![Flake8](https://img.shields.io/badge/flake8-enabled-yellowgreen)](https://flake8.pycqa.org/) 7 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) 8 | [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/BRGM/gml_application_schema_toolbox/master.svg)](https://results.pre-commit.ci/latest/github/BRGM/gml_application_schema_toolbox/master) 9 | 10 | This QGIS 3 plugin provide the capabilities to: 11 | 12 | * Download GML from WFS 2 services 13 | * Read GML App Schema files in XML mode 14 | * Convert GML App Schema files in PostGIS and SQLite format 15 | * Export PostGIS and SQLite format to GML App Schema files 16 | 17 | The technical and user documentation is here: 18 | 19 | ![](docs/static/img/overview.png)![](static/img/overview.png) 20 | 21 | ---- 22 | 23 | ## Authors 24 | 25 | The plugin has been funded by: 26 | 27 | * [BRGM](http://www.brgm.eu/) - BRGM is involved for a long time in the definition of interoperability standards especially linked to OGC and the European INSPIRE directive initiatives. 28 | * [European Union's Earth observation programme Copernicus](http://www.copernicus.eu/), as part of the tasks delegated to the European Environment Agency 29 | 30 | The plugin has been developed by [Oslandia](https://www.oslandia.com/), [Camptocamp](https://www.camptocamp.com/) and rely on [OGR GMLAS driver](https://www.gdal.org/drv_gmlas.html) developed by [Spatialys](https://www.spatialys.com/). 31 | 32 | Thanks to all [contributors](https://github.com/BRGM/gml_application_schema_toolbox/graphs/contributors). 33 | 34 | ---- 35 | 36 | ## License 37 | 38 | The project license is [GPLv2+](LICENSE). 39 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "70...100" 8 | status: 9 | project: off 10 | patch: off 11 | 12 | parsers: 13 | gcov: 14 | branch_detection: 15 | conditional: yes 16 | loop: yes 17 | method: no 18 | macro: no 19 | 20 | comment: 21 | layout: "reach,diff,flags,tree" 22 | behavior: default 23 | require_changes: no 24 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | 3 | """ 4 | Configuration for project documentation using Sphinx. 5 | """ 6 | 7 | # standard 8 | import sys 9 | from os import environ, path 10 | 11 | sys.path.insert(0, path.abspath("..")) 12 | 13 | # Package 14 | from gml_application_schema_toolbox import __about__ 15 | 16 | # -- Build environment ----------------------------------------------------- 17 | on_rtd = environ.get("READTHEDOCS", None) == "True" 18 | 19 | # -- Project information ----------------------------------------------------- 20 | project = __about__.__title__ 21 | author = __about__.__author__ 22 | copyright = __about__.__copyright__ 23 | version = release = __about__.__version__ 24 | github_doc_root = "{}/tree/master/doc/".format(__about__.__uri_repository__) 25 | 26 | myst_substitutions = { 27 | "title": project, 28 | "author": author, 29 | "repo_url": __about__.__uri__, 30 | "version": version, 31 | } 32 | 33 | myst_url_schemes = ("http", "https", "mailto") 34 | 35 | # -- General configuration --------------------------------------------------- 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = [ 41 | # Sphinx included 42 | "sphinx.ext.autodoc", 43 | "sphinx.ext.autosectionlabel", 44 | "sphinx.ext.extlinks", 45 | "sphinx.ext.githubpages", 46 | "sphinx.ext.intersphinx", 47 | "sphinx.ext.viewcode", 48 | # 3rd party 49 | "myst_parser", 50 | "sphinx_copybutton", 51 | ] 52 | 53 | 54 | # The suffix(es) of source filenames. 55 | # You can specify multiple suffix as a list of string: 56 | # 57 | source_suffix = {".md": "markdown", ".rst": "restructuredtext"} 58 | autosectionlabel_prefix_document = True 59 | # The master toctree document. 60 | master_doc = "index" 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = "en" 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | # This pattern also affects html_static_path and html_extra_path . 72 | exclude_patterns = ["_build", ".venv", "Thumbs.db", ".DS_Store", "_output", "ext_libs"] 73 | 74 | # The name of the Pygments (syntax highlighting) style to use. 75 | pygments_style = "sphinx" 76 | 77 | 78 | # -- Options for HTML output ------------------------------------------------- 79 | 80 | # -- Theme 81 | 82 | html_favicon = ( 83 | "../gml_application_schema_toolbox/resources/gml_application_schema_toolbox.png" 84 | ) 85 | html_logo = ( 86 | "../gml_application_schema_toolbox/resources/gml_application_schema_toolbox.png" 87 | ) 88 | html_static_path = ["static"] 89 | html_theme = "furo" 90 | # html_theme_options = { 91 | # "github_url": __about__.__uri_repository__, 92 | # "repository_url": __about__.__uri_repository__, 93 | # } 94 | 95 | 96 | myst_enable_extensions = [ 97 | "deflist", 98 | "html_image", 99 | "smartquotes", 100 | "replacements", 101 | "linkify", 102 | "substitution", 103 | ] 104 | # -- EXTENSIONS -------------------------------------------------------- 105 | 106 | # Configuration for intersphinx (refer to others docs). 107 | intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} 108 | 109 | # -- Options for Sphinx API doc ---------------------------------------------- 110 | 111 | 112 | # run api doc 113 | def run_apidoc(_): 114 | from sphinx.ext.apidoc import main 115 | 116 | cur_dir = path.normpath(path.dirname(__file__)) 117 | output_path = path.join(cur_dir, "_apidoc") 118 | modules = str(__about__.DIR_PLUGIN_ROOT.resolve()) 119 | exclusions = ["../.venv", "../.github", "../tests"] 120 | main(["-e", "-f", "-M", "-o", output_path, modules] + exclusions) 121 | 122 | 123 | # launch setup 124 | def setup(app): 125 | app.connect("builder-inited", run_apidoc) 126 | -------------------------------------------------------------------------------- /docs/development/contributing.md: -------------------------------------------------------------------------------- 1 | ```{include} ../../CONTRIBUTING.md 2 | ``` 3 | -------------------------------------------------------------------------------- /docs/development/documentation.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | This documentation website is generated using Sphinx and deployed on [GitHub pages](https://pages.github.com/) using GitHub Actions. 4 | 5 | To work on the documentation, install the related requirements: 6 | 7 | ```bash 8 | python -m pip install -U -r requirements/documentation.txt 9 | ``` 10 | 11 | ## Write 12 | 13 | The documentation is divided into categories. To update a particular page, find the markdown file in the related folder: 14 | 15 | - for end-users: `docs/usage` 16 | - for developers: `docs/development` 17 | 18 | Put images in the `assets` directory. 19 | 20 | ```{tip} 21 | To see live rendering of your documentation, you can run: `sphinx-autobuild -b html docs docs/_build/html`. 22 | ``` 23 | 24 | After you make changes to the documentation, please make a PR. 25 | 26 | ## Build 27 | 28 | ```bash 29 | # build it 30 | sphinx-build -b html docs docs/_build/html 31 | ``` 32 | 33 | Open `docs/_build/index.html` in a web browser. 34 | 35 | ## Deploy 36 | 37 | Documentation website is hosted on GitHub Pages. Deployment takes advantage of [`ghp-import` library](https://pypi.org/project/ghp-import/). It's automatically triggered on CI but it's still possible to deploy it manually: 38 | 39 | ```bash 40 | ghp-import --force --no-jekyll --push docs/_build/html 41 | ``` 42 | 43 | Files are uploaded to the branch `gh-pages` of the repository: . 44 | -------------------------------------------------------------------------------- /docs/development/environment.md: -------------------------------------------------------------------------------- 1 | # Development environment setup 2 | 3 | ## Requirements 4 | 5 | - Python 3.7 or later 6 | - QGIS LTR 7 | - PostgreSQL with PostGIS extension (or Docker) 8 | - GDAL 9 | 10 | ---- 11 | 12 | ## Code and software 13 | 14 | Clone the repository, then follow these steps. 15 | 16 | It's strongly recomended to develop into a virtual environment: 17 | 18 | ```bash 19 | python3 -m venv --system-site-packages .venv 20 | ``` 21 | 22 | ```{note} 23 | We link to system packages to keep the link with installed QGIS Python libraries: PyQGIS, PyQT, etc. 24 | ``` 25 | 26 | In your virtual environment: 27 | 28 | ```bash 29 | # use the latest pip version 30 | python -m pip install -U pip setuptools wheel 31 | # install development tools 32 | python -m pip install -U -r requirements/development.txt 33 | # install pre-commit to respect common development guidelines 34 | pre-commit install 35 | ``` 36 | 37 | ---- 38 | 39 | ## Data 40 | 41 | A PostGIS database is required. A dead simple [docker compose file](https://github.com/BRGM/gml_application_schema_toolbox/blob/master/tests/dev/docker-compose_postgis.yml) is provided into the `tests/dev/` subfolder. You can run it with: 42 | 43 | ```bash 44 | docker-compose -f "tests/dev/docker-compose_postgis.yml" up -d --build 45 | ``` 46 | 47 | Look at the `tests/samples` and `tests/fixtures` subfolders for sample data. You can find some useful commands into the [Testing GDAL section](../usecases/index). 48 | -------------------------------------------------------------------------------- /docs/development/history.md: -------------------------------------------------------------------------------- 1 | ```{include} ../../CHANGELOG.md 2 | ``` 3 | -------------------------------------------------------------------------------- /docs/development/testing.md: -------------------------------------------------------------------------------- 1 | # Testing the plugin 2 | 3 | Tests are written in 2 separate folders: 4 | 5 | - `tests/unit`: testing code which is independent of QGIS API 6 | - `tests/qgis`: testing code which depends on QGIS API 7 | 8 | ## Requirements 9 | 10 | - QGIS 3.16+ 11 | 12 | ```bash 13 | python -m pip install -U pip 14 | python -m pip install -U -r requirements/testing.txt 15 | ``` 16 | 17 | ## Running tests 18 | 19 | ```bash 20 | # run all tests with PyTest and Coverage report 21 | python -m pytest 22 | 23 | # run only unit tests 24 | python -m pytest tests/unit 25 | 26 | # run only QGIS tests 27 | python -m pytest tests/qgis 28 | 29 | # run a specific test module using standard unittest 30 | python -m unittest tests.test_plg_metadata 31 | 32 | # run a specific test function using standard unittest 33 | python -m unittest tests.test_plg_metadata.TestPluginMetadata.test_version_semver 34 | ``` 35 | 36 | ### Using Docker 37 | 38 | Build the image: 39 | 40 | ```bash 41 | docker build --pull --rm -f "tests/tests_qgis.dockerfile" -t qgis_316:plugin_tester . 42 | ``` 43 | 44 | Run tests: 45 | 46 | ```bash 47 | docker run -v "$(pwd):/tmp/plugin" qgis_316:plugin_tester python3 -m pytest 48 | ``` 49 | 50 | Please note that will use the root rights on some folders. 51 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ```{include} ../README.md 2 | ``` 3 | 4 | ---- 5 | 6 | ## Context 7 | 8 | In the context of the definition of interoperability standards especially linked to OGC and the European INSPIRE directive initiatives, existing tools being limited for an easy exploitation of these standards, this project aims at developing a QGIS plugin relying on OGR library to deal with GML Application Schema datasets. 9 | 10 | In particular, the aim is to develop tools to **manipulate Complex Features streams in a GIS desktop application**. 11 | 12 | [ISO19109](http://www.iso.org/iso/catalogue_detail.htm?csnumber=39891) defines in its General Feature Model, the notion of FeatureType. Each domain can define its own application schema of FeatureTypes by reusing or extending the base types. Thus, used to describe and exchange domain related content, a FeatureType based information flow is often really rich in its datastructure. The new data structure often leads to the generation of a XSDs; basis of XML exchanges (eg. [INSPIRE data specification XSD](http://inspire.ec.europa.eu/XML-Schemas/Data-Specifications/2892)). “Complex Feature” term is used as opposed to “Simple Feature” (cf. [OGC® 10-100r3](http://portal.opengeospatial.org/files/?artifact_id=42729)), a subset of XML-Schema and GML to lower the “implementation bar” restricting spatial/non-spatial property types, cardinality of properties... 13 | 14 | Complex Features streams are natively represented by an XML content which allows, thanks to its hierarchical structure, to express an instance coming from a rich object model. Although being developed and tested on a fixed subset of application schemas, this project aims at being generic and adaptable to any (valid) application schema. We do not want to limit a priori the rich possibilities offered by the Complex Features object model. Possible problems of performances and limit in model complexity will have to be determined as soon as possible. 15 | 16 | ---- 17 | 18 | ## Example & samples 19 | 20 | Most of the example below are based on: 21 | 22 | * INSPIRE Environmental Monitoring Facility WFS flow on BRGM piezometers, 23 | * GroundWaterML2.0 WFS flow on French aquifer reference dataset (BD LISA), 24 | * SOS flows on the groundwater level measurements acquired by the piezometers monitoring those aquifers. 25 | 26 | ---- 27 | 28 | ## Documentation contents 29 | 30 | ```{toctree} 31 | --- 32 | caption: Use the plugin 33 | maxdepth: 1 34 | --- 35 | usage/quickstart 36 | usage/download 37 | usage/read_files 38 | usage/read_custom 39 | usage/read_mode_db 40 | usage/read_mode_xml 41 | usage/write_from_db 42 | usage/settings 43 | ``` 44 | 45 | ```{toctree} 46 | --- 47 | caption: Use cases 48 | maxdepth: 1 49 | --- 50 | usecases/index 51 | usecases/UC_vbox.md 52 | usecases/UC_overlap.md 53 | usecases/UC_codelist.md 54 | usecases/UC_create_inspire_data.md 55 | ``` 56 | 57 | ```{toctree} 58 | --- 59 | caption: Contribute to the plugin 60 | maxdepth: 1 61 | --- 62 | Code documentation <_apidoc/modules> 63 | development/contributing 64 | development/environment 65 | development/testing 66 | development/documentation 67 | development/history 68 | ``` 69 | 70 | ```{toctree} 71 | --- 72 | caption: Testing 73 | maxdepth: 1 74 | --- 75 | testing/scenario_mode_xml_local 76 | testing/scenario_mode_gmlas_local 77 | testing/scenario_mode_xml_content_negociation 78 | ``` 79 | -------------------------------------------------------------------------------- /docs/presentations/2017_FOSS4G-E/20170718_QGIS_GMLAS_toolbox_groundwater_monitoring_BRGM_OSLANDIA.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/presentations/2017_FOSS4G-E/20170718_QGIS_GMLAS_toolbox_groundwater_monitoring_BRGM_OSLANDIA.pptx -------------------------------------------------------------------------------- /docs/presentations/2017_FOSS4G-E/GML application schema made easy in GDAL_OGR and QGIS - GMLAS driver.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/presentations/2017_FOSS4G-E/GML application schema made easy in GDAL_OGR and QGIS - GMLAS driver.pdf -------------------------------------------------------------------------------- /docs/presentations/2017_FOSS4G-E/GML workshop_intro_chris_sylvain.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/presentations/2017_FOSS4G-E/GML workshop_intro_chris_sylvain.pptx -------------------------------------------------------------------------------- /docs/presentations/2017_FOSS4G-E/ReadMe.txt: -------------------------------------------------------------------------------- 1 | Presentations from the workshop held 2 | -------------------------------------------------------------------------------- /docs/presentations/2018_EGU/Demo_1_2_0_rc2_EPOS_WP15_EGU_with_datagraph.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/presentations/2018_EGU/Demo_1_2_0_rc2_EPOS_WP15_EGU_with_datagraph.mp4 -------------------------------------------------------------------------------- /docs/presentations/2018_INSPIRE_conference/1.2.0_video_INSPIRE_PPI_conf_2018.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/presentations/2018_INSPIRE_conference/1.2.0_video_INSPIRE_PPI_conf_2018.mp4 -------------------------------------------------------------------------------- /docs/static/img/download-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/download-data.png -------------------------------------------------------------------------------- /docs/static/img/download-from-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/download-from-url.png -------------------------------------------------------------------------------- /docs/static/img/download-service-bad-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/download-service-bad-url.png -------------------------------------------------------------------------------- /docs/static/img/download-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/download-service.png -------------------------------------------------------------------------------- /docs/static/img/install-plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/install-plugin.png -------------------------------------------------------------------------------- /docs/static/img/ogr-mapping-long-table-names.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/ogr-mapping-long-table-names.png -------------------------------------------------------------------------------- /docs/static/img/ogr-options-RemoveUnusedLayers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/ogr-options-RemoveUnusedLayers.png -------------------------------------------------------------------------------- /docs/static/img/ogr-options-xlink-RegistryResolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/ogr-options-xlink-RegistryResolution.png -------------------------------------------------------------------------------- /docs/static/img/overview-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/overview-toolbar.png -------------------------------------------------------------------------------- /docs/static/img/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/overview.png -------------------------------------------------------------------------------- /docs/static/img/plugin_gmlas_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/plugin_gmlas_menu.png -------------------------------------------------------------------------------- /docs/static/img/plugin_gmlas_settings_into_qgis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/plugin_gmlas_settings_into_qgis.png -------------------------------------------------------------------------------- /docs/static/img/qgis-relations-discovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/qgis-relations-discovery.png -------------------------------------------------------------------------------- /docs/static/img/qgis_macros_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/qgis_macros_warning.png -------------------------------------------------------------------------------- /docs/static/img/qgis_macros_warning_option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/qgis_macros_warning_option.png -------------------------------------------------------------------------------- /docs/static/img/read-custom-timeseries-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-custom-timeseries-view.png -------------------------------------------------------------------------------- /docs/static/img/read-custom-timeseries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-custom-timeseries.png -------------------------------------------------------------------------------- /docs/static/img/read-db-gmlas-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-gmlas-config.png -------------------------------------------------------------------------------- /docs/static/img/read-db-gmlas-extent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-gmlas-extent.png -------------------------------------------------------------------------------- /docs/static/img/read-db-gmlas-otheroptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-gmlas-otheroptions.png -------------------------------------------------------------------------------- /docs/static/img/read-db-gmlas-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-gmlas-source.png -------------------------------------------------------------------------------- /docs/static/img/read-db-gmlas-target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-gmlas-target.png -------------------------------------------------------------------------------- /docs/static/img/read-db-gmlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-gmlas.png -------------------------------------------------------------------------------- /docs/static/img/read-db-schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-db-schema.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-attributetable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-attributetable.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-featureinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-featureinfo.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-featureinfoform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-featureinfoform.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-layerprops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-layerprops.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-resolver-obs-after-insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-resolver-obs-after-insert.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-resolver-obs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-resolver-obs.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-resolver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-resolver.png -------------------------------------------------------------------------------- /docs/static/img/read-xml-xpath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/read-xml-xpath.png -------------------------------------------------------------------------------- /docs/static/img/testing/1.GMLAS_information_seed_displayed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/1.GMLAS_information_seed_displayed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/1.GMLAS_load_layer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/1.GMLAS_load_layer.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/1.information_seed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/1.information_seed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/1.information_seed_displayed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/1.information_seed_displayed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/2.GMLAS_de_rereferencing_vocabulary_filled.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/2.GMLAS_de_rereferencing_vocabulary_filled.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/2.GMLAS_de_rereferencing_vocabulary_filled_and_displayed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/2.GMLAS_de_rereferencing_vocabulary_filled_and_displayed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/2.de_rereferencing_vocabulary.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/2.de_rereferencing_vocabulary.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/2.de_rereferencing_vocabulary_filled.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/2.de_rereferencing_vocabulary_filled.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/3.GMLAS_Geology_log_filled.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/3.GMLAS_Geology_log_filled.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/3.GMLAS_Geology_log_filled_and_displayed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/3.GMLAS_Geology_log_filled_and_displayed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/3.GMLAS_Geology_log_load_layer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/3.GMLAS_Geology_log_load_layer.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/3.Geology_log_viewer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/3.Geology_log_viewer.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/3.sos_observationData.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/3.sos_observationData.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/5.SOS_TimeSeries_filled.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/5.SOS_TimeSeries_filled.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/5.SOS_TimeSeries_filled_and_displayed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/5.SOS_TimeSeries_filled_and_displayed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/5.SOS_TimeSeries_load_layer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/5.SOS_TimeSeries_load_layer.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/5.Timeseries_viewer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/5.Timeseries_viewer.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/5_sos_GetObservationResponse.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/5_sos_GetObservationResponse.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/6.GMLAS_GWML2_filled.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/6.GMLAS_GWML2_filled.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/6.GMLAS_GWML2_filled_and_displayed.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/6.GMLAS_GWML2_filled_and_displayed.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/6.GMLAS_HydroGeoUnit_load_layer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/6.GMLAS_HydroGeoUnit_load_layer.PNG -------------------------------------------------------------------------------- /docs/static/img/testing/6.GWML2_ressource_added.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/docs/static/img/testing/6.GWML2_ressource_added.PNG -------------------------------------------------------------------------------- /docs/testing/scenario_mode_xml_content_negociation.md: -------------------------------------------------------------------------------- 1 | # XML Mode - Content negociation test scenario 2 | 3 | This scenario uses URI as opposed to files stored on GitHub thus involves content negociation with data servers 4 | 5 | ## 1. Set up project and make some checks 6 | 7 | Same steps 1 to 4 as for the [XML Mode - Local test scenario](scenario_mode_xml_local) 8 | 9 | ## 2. dereferencing another Feature (a GroundWater Quantity Monitoring Facility) 10 | 11 | Same steps as for the "XML Mode - Local test scenario" 12 | TODO SG : tweak the resolver conf to make this work 13 | 14 | ## 3. from the Monitoring facility access groundwater observation 15 | 16 | Step skipped for now as contrary to the "XML Mode - Local test scenario", the payload describing the GroundWater Quantity Monitoring Facility served via the URI does not provide a reference to a SOS XML endpoint anymore (SensorThings API instead) 17 | 18 | ## 4. from the Monitoring Facility add the GroundWater ressource monitored 19 | 20 | Same steps as for the "XML Mode - Local test scenario" 21 | 22 | ## 5. interrogate the GroundWater ressource monitored 23 | 24 | Same steps as for the "XML Mode - Local test scenario" 25 | -------------------------------------------------------------------------------- /docs/testing/scenario_mode_xml_local.md: -------------------------------------------------------------------------------- 1 | # XML Mode - local test scenario 2 | 3 | This scenario uses files stored on GitHub to avoid potential content negociation issues (network issues) with data servers 4 | 5 | ## 1. Add a WMS background image 6 | 7 | Whatever suits you, provided the entire world is visible (will help detect X/Y Axis being flipped). 8 | 9 | ## 2. Initial information seed 10 | 11 | Copy the URL to the [initial information seed](https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/master/tests/basic_test_scenario/0_BoreholeView.xml): 12 | 13 | ```txt 14 | https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/master/tests/basic_test_scenario/0_BoreholeView.xml 15 | ``` 16 | 17 | Then: 18 | 19 | 1. `Load wizard` 20 | 2. `File/Url` > `Load in XML Mode` 21 | 3. `XML Options : None` 22 | 23 | **Expected result:** 1 new QGIS layer (BoreholeView (shape)) 24 | 25 | ![information_seed_displayed](../static/img/testing/1.information_seed_displayed.PNG) 26 | 27 | Using the QGIS tool `Identify Features` on the point added, features attributes from the GML **shall** be displayed. 28 | 29 | ## 3. Dereferencing vocabulary 30 | 31 | ### On INSPIRE registry 32 | 33 | Locate `gsmlp:purpose/@xlink:href` and: 34 | 35 | 1. `Right click` 36 | 2. `Resolve external` > `Embedded` 37 | 38 | **Expected result:** the content of the attribute **shall** be enriched with content coming from the INSPIRE registry 39 | 40 | ![INSPIRE-registry-response](../static/img/testing/2.de_rereferencing_vocabulary_filled.PNG) 41 | 42 | ### On OGC definition server 43 | 44 | Proceed as above on attributes having `xlink:href` starting with `http://www.opengis.net/def/`. 45 | 46 | ### On EU geological surveys linked data registry 47 | 48 | Proceed as above on attributes having `@xlink:href` starting with `http://data.geoscience.earth/ncl/`. 49 | 50 | ## 4. Dereferencing a 1st feature (a geological log) 51 | 52 | Locate `gsmlp:geologicalDescription/@xlink:href` and: 53 | 54 | 1. `Right click` 55 | 2. `Resolve external` > `Embedded` 56 | 57 | **Expected result:** the content of the attribute **shall** be enriched with `sos:observationData`. 58 | 59 | Open one of them and expand the `om:OM_Observation` then the `om:result` -> the Geology log viewer icon **shall** be proposed 60 | 61 | ![sos:observationData](../static/img/testing/3.sos_observationData.PNG) 62 | 63 | ### Use the Geology log viewer 64 | 65 | Clicking on the icon next to `GW_GeologyLog` **shall** launch the Geology log viewer (preconfigured to render OGC:GroundWaterML2.0 GeologyLogCoverage compliant content): 66 | 67 | ![GeologyLogViewer](../static/img/testing/3.Geology_log_viewer.PNG) 68 | 69 | ## 5. Dereferencing another Feature (a GroundWater Quantity Monitoring Facility) 70 | 71 | Locate `gsmlp:groundWaterLevel/@xlink:href`: 72 | 73 | 1. `Right click` 74 | 2. `Resolve external` > `As a new Layer` > Ticking `Swap X/Y` 75 | 76 | **Expected result:** two new QGIS layers (EnvironmentalMonitoringFacility (geometry) and EnvironmentalMonitoringFacility (representativePoint)) 77 | 78 | Using the QGIS tool `Identify Features` on the point added, features attributes from the GML **shall** be displayed. 79 | 80 | ## 6. From the Monitoring facility access groundwater observation 81 | 82 | Dereferencing hasObservation (the one whose title is `6512X0037 groundwater quantity observation collection (SOS and WaterML 2.0 format)`). 83 | 84 | Locate `ef:hasObservation/@xlink:href` and: 85 | 86 | 1. `Right click` 87 | 2. `Resolve external` > `Embedded` 88 | 89 | **Expected result:** the content of the attribute **shall** be enriched with sos:GetObservationResponse 90 | 91 | Expending the om:OM_Observation then the om:result -> the Timeseries viewer icon **shall** be proposed 92 | 93 | ![sos:GetObservationResponse](../static/img/testing/5_sos_GetObservationResponse.PNG) 94 | 95 | ### Use the Timeseries viewer 96 | 97 | Clicking on the icon next to `wml2:MeasurementTimeseries` **shall** launch the TimeSeries viewer (preconfigured to render OGC:WaterML2.0 Part 1 Timeseries compliant content): 98 | 99 | ![Timeseries_viewer](../static/img/testing/5.Timeseries_viewer.PNG) 100 | 101 | ## 7. From the Monitoring Facility add the GroundWater ressource monitored 102 | 103 | Locate `ef:observingCapability/ef:ObservingCapability/ef:ultimateFeatureOfInterest/xlink@href` and: 104 | 105 | 1. `Right click` 106 | 2. `Resolve external` > `As a new Layer` 107 | 108 | **Expected result:** 1 new QGIS layer GW_ConfiningBed (shape). 109 | 110 | Move it under the other features using the 'Layers Panel' for better visibility: 111 | 112 | ![GWML2_ressource](../static/img/testing/6.GWML2_ressource_added.PNG) 113 | 114 | ## 8. interrogate the GroundWater ressource monitored 115 | 116 | Using the QGIS tool `Identify Features` on the point added, features attributes from the GML **shall** be displayed according to OGC GWML2 Model. 117 | -------------------------------------------------------------------------------- /docs/usage/download.md: -------------------------------------------------------------------------------- 1 | # Download GML from WFS 2 services 2 | 3 | Source document can be GML file already available or they can be downloaded from [WFS 2.0.0 OGC services](http://www.opengeospatial.org/standards/wfs). 4 | 5 | This part of the plugin is inspired by the work made in [QGIS WFS2 client plugin](https://github.com/JuergenWeichand/qgis-wfs20-client-plugin) but used owslib to communicate with the service. 6 | 7 | ## Connect to a service 8 | 9 | In the `download` tab define the target service to download data from: 10 | 11 | - `URL`: Define the service URL (without GetCapabilities). A list of sample services is available by activating the drop down. 12 | - Authentication: If the service requires user credentials (Basic Authentication), set the following parameters: 13 | - `Username` 14 | - `Password` 15 | 16 | ![Configure target WFS service](../static/img/download-service.png) 17 | 18 | Once set, connect to the service using the `Get capabilities` button. 19 | The `Show capabilities` provides a simple preview of the 20 | GetCapabilities document. After connection, the list of 21 | `Feature Types` and `Stored Queries` are populated. 22 | 23 | In case of error, a notification pops up with information about 24 | the problem. Check also the log panel for more details about the error: 25 | 26 | ![Download from bad URL](../static/img/download-service-bad-url.png) 27 | 28 | ## Download data 29 | 30 | To download data in GML format, set the following parameters: 31 | 32 | - Choose a `Feature type` or a `Stored Query` 33 | - `Filter by extent`: Define the bounding box of the area of interest 34 | - `Request options` 35 | - `Feature limit`: Define the maximum number of features to download 36 | - `Output`: Define the output filename (by default, a temporary file is created in the tmp folder) 37 | 38 | Click the `Download` button to start the download. 39 | 40 | ![Configure target feature type or stored query to download](../static/img/download-data.png) 41 | 42 | After download, the convert panel is activated with the path to the downloaded file. 43 | 44 | ## Download from URL 45 | 46 | The panel can also be used to download file from a URL. This URL 47 | could point to an existing GML file or could be a `GetFeature` request. 48 | 49 | ![Download from URL](../static/img/download-from-url.png) 50 | -------------------------------------------------------------------------------- /docs/usage/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | ## Plugin installation 4 | 5 | ### From QGIS 6 | 7 | Use [QGIS plugin manager](http://docs.qgis.org/2.14/en/docs/training_manual/qgis_plugins/fetching_plugins.html) to install the plugin from the repository. 8 | 9 | ![Plugin manager](../static/img/install-plugin.png) 10 | 11 | ### From sourcecode 12 | 13 | The plugin source code is available here: https://github.com/brgm/gml_application_schema_toolbox 14 | 15 | ---- 16 | 17 | ## Plugin main window 18 | 19 | To open the plugin panel, right click on the toolbars to add the `GML App Schema Toolbox` panel. 20 | 21 | ![Enable toolbar](../static/img/overview-toolbar.png) 22 | 23 | - [Download](DOWNLOAD.md) 24 | - [Read](READ.md) 25 | - [Read in XML mode](read_mode_xml.md) 26 | - [Read in database (relational) mode](read_mode_db.md) 27 | - [Browse custom elements](READ-CUSTOM.md) 28 | - [Write](WRITE-FROM-DB.md) 29 | -------------------------------------------------------------------------------- /docs/usage/read_custom.md: -------------------------------------------------------------------------------- 1 | # Custom element viewers 2 | 3 | Accessing the information contained in XML elements, either thanks to the native XML approach or thanks to the relational model approach is not always easy. Some elements may be visually displayed in a way that is more pleasant for the end user. 4 | We have tested the idea here of custom element viewers for certain known types. 5 | 6 | In particular, we have developed a custom viewer for the **time series of measurements** that can be found in the WaterML2 – part I : TimeSeries application schema. Such time series are represented by XML sequences of values. Plotting them as a curve should be a better representation than a raw list of time instants and values. 7 | 8 | The idea behind this custom viewer is to offer an API that could be used by third party developers to provide custom viewer widgets for some complex types. This API is developed so that it works regardless of the value passed for the `Maximum table merging depth`. 9 | 10 | The `time series` custom viewer is shipped with the current plugin. It is associated with `wml2:MeasurementTimeseries` elements. In both modes (native XML or relational), a button allows to display time series thanks to this custom viewer. 11 | 12 | In XML mode 13 | 14 | ![Time series button in XML mode](../static/img/read-custom-timeseries.png) 15 | 16 | Example of the display given by the WaterML2 time series viewer 17 | 18 | ![Time series view](../static/img/read-custom-timeseries-view.png) 19 | 20 | A custom viewer is made to be linked to a particular type (or table name). It receives in input a handle on the XML subtree or on the table and should return a Qt widget. 21 | -------------------------------------------------------------------------------- /docs/usage/read_files.md: -------------------------------------------------------------------------------- 1 | # Read GML App Schema files 2 | 3 | Manipulating GML complex features with QGIS revolves around two approaches, 4 | each having a dominating data representation mode: 5 | 6 | - a [native XML approach](read_mode_xml.md), where data are stored and manipulated directly in their XML hierarchical form. Each “Complex Feature” is associated with a single row in a vector layer. The user is in charge of identifying among the XML tree, which elements are of interest for its use case. 7 | 8 | - a [database (relational) approach](read_mode_db.md), where XML data are first stored in a database with relations between tables. The user then relies on native mechanisms found in QGIS to manipulate these data (joins, relations, form widgets). 9 | -------------------------------------------------------------------------------- /docs/usage/read_mode_db.md: -------------------------------------------------------------------------------- 1 | # Convert GML App Schema files in PostGIS and SQLite format 2 | 3 | ## Context 4 | 5 | In a relational approach, the main problem is to convert from an XML flow of Complex Features instances to a relational database representation. The approach we followed consists in: 6 | 7 | - analyzing the XSD schema(s) declared by the XML instance in order to retrieve the underlying object model and determine the type of each element and attribute as well as links between elements; 8 | - converting links between elements into relations between database tables; 9 | - inserting values in the database corresponding to the values found in the XML instance; 10 | - generating a QGIS project configured to easily manipulate the relational model. 11 | 12 | ## Using OGR GMLAS driver (default mode) 13 | 14 | [OGR GMLAS driver](http://www.gdal.org/drv_gmlas.html) is used to analyse XSDs and create the database model corresponding to the schema. 15 | 16 | Converting an XML instance into a relational SQL model generates numerous tables and relations between them. 17 | In order to expose the entire relational model inside QGIS, all the tables of the database must be loaded (including tables without geometries) and links between them must be declared. 18 | QGIS has a concept of “relations” that allows to declare 1:N relations between vector layers. These relations can then be used in the form view of layers to navigate the model. 19 | The plugin developed automatically generates a QGIS project with all the layers loaded and all the known relations declared. 20 | 21 | The plugin configuration is the following: 22 | 23 | ![GMLAS panel](../static/img/read-db-gmlas.png) 24 | 25 | First, define the input document to convert to a database. 26 | The input can be: 27 | 28 | - A GML file available on the computer 29 | 30 | - A URL pointing to a GML file 31 | 32 | - An XSD file 33 | 34 | In this last case, the XSD is analyzed to create the database 35 | structure with no data. 36 | 37 | ![GML or XSD source](../static/img/read-db-gmlas-source.png) 38 | 39 | Then define the configuration on how to read the GML or XSD file. 40 | 41 | The conversion is made by OGR and its GMLAS driver which provides 42 | advanced configuration using an XML file. Check the [GMLAS driver XSD](https://svn.osgeo.org/gdal/trunk/gdal/data/gmlasconf.xsd) 43 | to discover all options available. 44 | 45 | The plugin provides 2 configuration files: 46 | 47 | - `gmlasconf.xml`: which is the default 48 | 49 | - `gmlasconf-inspire.xml`: which provide some specific configuration related to INSPIRE (ie. XLink resolution with the INSPIRE Registry) 50 | 51 | Based on this configuration file, then some extra options are also defined in the user interface: 52 | 53 | - `Remove unused layers` will clean up empty tables at the end of the process 54 | 55 | - `Remove unused fields` will clean up empty columns at the end of the process 56 | 57 | Those options are defined in the XML configuration but are overriden by the plugin when starting the conversion. 58 | 59 | Click on the `Load layer list` button to analyze the GML and its XSD to list all layers availables. 60 | 61 | ![GMLAS config](../static/img/read-db-gmlas-config.png) 62 | 63 | User can optionally restrict to an area of interest by selecting the area on the map canvas: 64 | 65 | ![GMLAS config](../static/img/read-db-gmlas-extent.png) 66 | 67 | More advanced options allow: 68 | 69 | - `Create OGR metadata tables`: OGR metadata tables. Those tables are required if you plan to write the database content back to GML. 70 | 71 | - `Create null constraint`: Creation of database null checks. Turn this off to be less strict eg. when GML file may contains null values on XSD non null elements. 72 | 73 | - `Convert to linear geometry`: Enable it to convert non-linear geometries types into linear geometries by approximating them. This is useful when database geometry type is more strict than the GML one. eg. enable it if 'Cannot create geometry field of type MULTISURFACE' 74 | 75 | - `Swap coordinates`: Depending on the spatial reference system (SRS), coordinate order is defined by the SRS code. It may happen that x and y are in wrong order as defined by the SRS (ie. the file is probably invalid). Use this option to swap coordinates. 76 | 77 | - `Skip failures`: Continue after a failure, skipping the failed feature. Better not to use it for better performance. 78 | 79 | - `Language`: For XLink resolution (eg. resources resolved from INSPIRE registry), define which language to use in HTTP header 'Accep-Language'. 80 | 81 | ![GMLAS config](../static/img/read-db-gmlas-otheroptions.png) 82 | 83 | Finally, define the target database: 84 | 85 | ![GMLAS config](../static/img/read-db-gmlas-target.png) 86 | 87 | ## Relational schema visualisation 88 | 89 | The plugin also exposes a dialog that allows to display the relational model created. It is graphically represented by the different tables with links between them represented as arrows. 90 | 91 | ![Feature form](../static/img/read-db-schema.png) 92 | 93 | On each table, a button allows to directly open the attribute table of the given layer. 94 | 95 | For the time being only file opened with PyXB mode can use 96 | the schema view. By future work, schema view may also 97 | support any databases schema (eg. PostGIS & SQLite created by the GMLAS driver). 98 | -------------------------------------------------------------------------------- /docs/usage/settings.md: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | ```{info} 4 | Settings have been renamed since version 1.4. 5 | ``` 6 | 7 | ## Global 8 | 9 | | Old name | New name | Visible | Settable | Type | Default | 10 | | -------- | ---------- | :-----: | :------: | :-----: | :-----: | 11 | | | debug_mode | X | X | boolean | False | 12 | | | version | X | | string | plugin version | 13 | 14 | ---- 15 | 16 | ## Network 17 | 18 | | Old name | New name | Visible | Settable | Type | Default | 19 | | ------------------- | ----------------------- | :-----: | :------: | :-----: | ------- | 20 | | default_language | network_language | X | X | string | en | 21 | | default_maxfeatures | network_max_features | X | X | int | 100 | 22 | | http_user_agent | network_http_user_agent | X | X | string | plugin name and version | 23 | 24 | ---- 25 | 26 | ## Usage 27 | 28 | | Old name | New name | Visible | Settable | Type | Default | 29 | | ------------------- | -------------------- | :-----: | :------: | :-----: | ------- | 30 | | default_access_mode | impex_access_mode | | create | string | | 31 | | default_db_type | impex_db_type | X | X | string | SQLite | 32 | | default_gmlas_config | impex_gmlas_config | | | string | gmlas | 33 | | last_import_method | impex_import_method | X | X | string | | 34 | | last_downloaded_file | last_downloaded_file | X | X | string | | 35 | | last_downloaded_path | last_downloaded_path | X | X | string | | 36 | | last_file | last_file | X | X | string | | 37 | | last_path | last_path | X | X | string | | 38 | | last_source | last_source | X | X | string | | 39 | 40 | ---- 41 | 42 | ## Related QGIS Settings 43 | 44 | ### Disable macros warning 45 | 46 | Switching to the `Featuer form` in `Identify` leads to a warning popup: 47 | 48 | ![QGIS - Macro warning popup](../static/img/qgis_macros_warning.png "QGIS - Macro warning popup") 49 | 50 | If you don't want to see this warning, it's possible to disable it in QGIS `Settings` > `Project Files` : 51 | 52 | ![QGIS - Macro warning option](../static/img/qgis_macros_warning_option.png "QGIS - Macro warning option") 53 | 54 | > It might be a security issue. See the documentation. 55 | -------------------------------------------------------------------------------- /docs/usage/write_from_db.md: -------------------------------------------------------------------------------- 1 | # Export PostGIS and SQLite format to GML App Schema files 2 | 3 | > TO DOC 4 | -------------------------------------------------------------------------------- /docs/usecases/UC_codelist.md: -------------------------------------------------------------------------------- 1 | # Codelist resolution based on user language 2 | 3 | This video shows how to convert biogeographical region into Spatialite 4 | format and how to use the [INSPIRE Registry](http://inspire.ec.europa.eu/registry/) 5 | to add codelist values and definitions. 6 | 7 | - configure conversion from GML into sqlite for BR INSPIRE theme dataset 8 | - use custom GMLAS configuration file in order to show how to use the 9 | INSPIRE Registry to retrieve codelist values and definitions. 10 | - show attribute table with resolved attributes 11 | - highlight how the Registry provides multilingual information 12 | 13 | - 14 | - 15 | 16 | - replay conversion with another language and show the results 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/usecases/UC_overlap.md: -------------------------------------------------------------------------------- 1 | # Combine datasets from different countries on the same map 2 | 3 | Based on Mappedfeature download data from different WFS and combine them on one map: 4 | 5 | - GeoSciML WFS from Sweden: 6 | - GeoSciML WFS from Finland: 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/usecases/UC_vbox.md: -------------------------------------------------------------------------------- 1 | # Setting up the virtual box with QGIS3 2 | 3 | Learn how to setup QGIS GML Application Schema virtual box: 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/usecases/convert-samples.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Creating POSTGRES DB schemas ..." 4 | psql -U qgis -d inspire -f db.sql 5 | 6 | echo "Moving to data folder ..." 7 | cd /home/qgis/qgisgmlas/data 8 | 9 | echo "Removing old SQLITE dbs ..." 10 | rm -fr db/*.sqlite 11 | 12 | 13 | echo "Converting samples ..." 14 | l=( 15 | 'inspire/BR/bioGeographicalRegion.gml:br:no' 16 | 'inspire/GE/geologicalunit.gml:ge:yes' 17 | 'inspire/LC/lcvLandCoverDataset.gml:lc:yes' 18 | 'inspire/LC/lcvLandCoverUnit.gml:lc:yes' 19 | 'inspire/PS/cddaDesignatedArea.gml:ps:yes' 20 | 'inspire/national/NL/cadastralparcel.gml:nl:auto' 21 | 'inspire/national/NL/cadastralboundary.gml:nl:auto' 22 | 'geology/AQD_UBA_oneOffering.xml:geology:auto' 23 | 'geology/AQD_UBA_Sample_50.xml:geology:auto' 24 | 'geology/AQD_UBA_SamplingPoint_50.xml:geology:no' 25 | 'geology/AQD_UBA_Station_50.xml:geology:auto' 26 | 'geology/BRGM_environmental_monitoring_facility_piezometer_50.xml:geology:auto' 27 | 'geology/BRGM_raw_database_observation_waterml2_output.xml:geology:auto' 28 | ) 29 | CONFIGFOLDER=/home/qgis/qgisgmlas/sourcecode/gml_application_schema_toolbox/gml_application_schema_toolbox/conf/ 30 | #CONFIGFILE=$CONFIGFOLDER/gmlasconf-inspire.xml 31 | CONFIGFILE=$CONFIGFOLDER/gmlasconf-inspire-cleanunused.xml 32 | #DEBUG="--config CPL_CURL_VERBOSE YES --debug on" 33 | DEBUG="" 34 | 35 | for index in "${l[@]}" ; do 36 | KEY=$(echo $index | cut -f1 -d:) 37 | VALUE=$(echo $index | cut -f2 -d:) 38 | SWAPCOORDS=$(echo $index | cut -f3 -d:) 39 | echo " $KEY > $VALUE ..." 40 | echo " ... to sqlite format ..." 41 | ogr2ogr db/$VALUE.sqlite GMLAS:$KEY \ 42 | -f sqlite -append \ 43 | $DEBUG -nlt CONVERT_TO_LINEAR \ 44 | -dsco spatialite=yes \ 45 | -oo swap_coordinates=$SWAPCOORDS \ 46 | -oo EXPOSE_METADATA_LAYERS=YES \ 47 | -oo CONFIG_FILE=$CONFIGFILE 48 | 49 | echo " ... to PostGIS format ..." 50 | ogr2ogr PG:'host=localhost user=qgis password=qgis dbname=inspire' \ 51 | GMLAS:$KEY \ 52 | -f PostgreSQL \ 53 | $DEBUG -nlt CONVERT_TO_LINEAR \ 54 | -overwrite -lco SCHEMA=$VALUE -lco LAUNDER=NO \ 55 | -oo swap_coordinates=$SWAPCOORDS \ 56 | -oo EXPOSE_METADATA_LAYERS=YES \ 57 | -oo CONFIG_FILE=$CONFIGFILE 58 | 59 | done 60 | echo "Done." 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /docs/usecases/db.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA IF NOT EXISTS BR AUTHORIZATION qgis; 2 | CREATE SCHEMA IF NOT EXISTS GE AUTHORIZATION qgis; 3 | CREATE SCHEMA IF NOT EXISTS LC AUTHORIZATION qgis; 4 | CREATE SCHEMA IF NOT EXISTS PS AUTHORIZATION qgis; 5 | CREATE SCHEMA IF NOT EXISTS NL AUTHORIZATION qgis; 6 | CREATE SCHEMA IF NOT EXISTS TEST AUTHORIZATION qgis; 7 | CREATE SCHEMA IF NOT EXISTS GEOLOGY AUTHORIZATION qgis; 8 | -------------------------------------------------------------------------------- /docs/usecases/ogr_relationships_to_fk.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE OR REPLACE FUNCTION ogr_add_fk_from_relationships(schema TEXT) 3 | RETURNS integer AS 4 | $BODY$ 5 | DECLARE 6 | t RECORD; 7 | r RECORD; 8 | q TEXT; 9 | aq TEXT; 10 | BEGIN 11 | RAISE NOTICE 'Converting OGR relationships to database foreign keys ...'; 12 | FOR t IN 13 | SELECT table_schema, table_name 14 | FROM information_schema.tables 15 | WHERE table_name = '_ogr_layer_relationships' AND 16 | table_schema = $1 17 | ORDER BY table_schema,table_name 18 | LOOP 19 | RAISE NOTICE 'Processing %.% ...', t.table_schema, t.table_name; 20 | q := ( 21 | 'SELECT parent_layer, parent_pkid, child_layer, child_pkid ' 22 | || 'FROM ' || t.table_schema || '.' || t.table_name 23 | ); 24 | FOR r IN EXECUTE q 25 | LOOP 26 | BEGIN 27 | RAISE NOTICE 'Creating FK on %.%', r.parent_layer, r.parent_pkid; 28 | aq := ( 29 | 'ALTER TABLE ' || t.table_schema || '.' || r.parent_layer || 30 | ' ADD CONSTRAINT ' || r.parent_layer || r.parent_pkid || '_uk ' || 31 | ' UNIQUE (' || r.parent_pkid || ')' 32 | ); 33 | EXECUTE aq; 34 | EXCEPTION WHEN OTHERS THEN 35 | RAISE NOTICE ' Constraints already exists.'; 36 | RAISE NOTICE ' [%] %', SQLSTATE, SQLERRM; 37 | END; 38 | -- Create FK 39 | BEGIN 40 | aq := ( 41 | 'ALTER TABLE ' || t.table_schema || '.' || r.child_layer || 42 | ' ADD CONSTRAINT ' || r.parent_layer || r.parent_pkid || 43 | ' FOREIGN KEY (' || r.child_pkid || ') ' || 44 | ' REFERENCES ' || t.table_schema || '.' || r.parent_layer || ' (' || r.parent_pkid || ')' 45 | ); 46 | EXECUTE aq; 47 | EXCEPTION WHEN OTHERS THEN 48 | RAISE NOTICE ' Error during constraint creation. Probably missing element in target table.'; 49 | RAISE NOTICE ' [%] %', SQLSTATE, SQLERRM; 50 | END; 51 | 52 | END LOOP; 53 | END LOOP; 54 | RETURN 1; 55 | END 56 | $BODY$ 57 | LANGUAGE 'plpgsql' ; 58 | 59 | 60 | CREATE OR REPLACE FUNCTION ogr_drop_fk_from_relationships(schema TEXT) 61 | RETURNS integer AS 62 | $BODY$ 63 | DECLARE 64 | t RECORD; 65 | r RECORD; 66 | q TEXT; 67 | aq TEXT; 68 | BEGIN 69 | RAISE NOTICE 'Converting OGR relationships to database foreign keys ...'; 70 | FOR t IN 71 | SELECT table_schema, table_name 72 | FROM information_schema.tables 73 | WHERE table_name = '_ogr_layer_relationships' AND 74 | table_schema = $1 75 | ORDER BY table_schema,table_name 76 | LOOP 77 | RAISE NOTICE 'Processing %.%', t.table_schema, t.table_name; 78 | q := ( 79 | 'SELECT parent_layer, parent_pkid, child_layer, child_pkid ' 80 | || 'FROM ' || t.table_schema || '.' || t.table_name 81 | ); 82 | FOR r IN EXECUTE q 83 | LOOP 84 | RAISE NOTICE 'Drop UK/FK on %.%', r.parent_layer, r.parent_pkid; 85 | aq := ( 86 | 'ALTER TABLE ' || t.table_schema || '.' || r.child_layer || 87 | ' DROP CONSTRAINT ' || r.parent_layer || r.parent_pkid 88 | ); 89 | EXECUTE aq; 90 | 91 | aq := ( 92 | 'ALTER TABLE ' || t.table_schema || '.' || r.parent_layer || 93 | ' DROP CONSTRAINT ' || r.parent_layer || r.parent_pkid || '_pk ' 94 | ); 95 | EXECUTE aq; 96 | 97 | END LOOP; 98 | END LOOP; 99 | RETURN 1; 100 | END 101 | $BODY$ 102 | LANGUAGE 'plpgsql' ; 103 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/__about__.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | """ 4 | Metadata about the package to easily retrieve informations about it. 5 | See: https://packaging.python.org/guides/single-sourcing-package-version/ 6 | """ 7 | 8 | from configparser import ConfigParser 9 | from datetime import date 10 | from pathlib import Path 11 | 12 | __all__ = [ 13 | "__author__", 14 | "__copyright__", 15 | "__email__", 16 | "__license__", 17 | "__summary__", 18 | "__title__", 19 | "__uri__", 20 | "__version__", 21 | ] 22 | 23 | # -- GLOBALS -------------------------------------------------------------------- 24 | 25 | DIR_PLUGIN_ROOT = Path(__file__).parent 26 | PLG_METADATA_FILE = DIR_PLUGIN_ROOT.resolve() / "metadata.txt" 27 | 28 | 29 | # -- FUNCTIONS -------------------------------------------------------------------- 30 | 31 | 32 | def plugin_metadata_as_dict() -> dict: 33 | """Read plugin metadata.txt and returns it as a Python dict. 34 | 35 | Raises: 36 | IOError: if metadata.txt is not found 37 | 38 | Returns: 39 | dict: dict of dicts. 40 | """ 41 | config = ConfigParser() 42 | if PLG_METADATA_FILE.is_file(): 43 | config.read(PLG_METADATA_FILE.resolve(), encoding="UTF-8") 44 | return {s: dict(config.items(s)) for s in config.sections()} 45 | else: 46 | raise IOError("Plugin metadata.txt not found at: %s" % PLG_METADATA_FILE) 47 | 48 | 49 | # -- VARIABLES -------------------------------------------------------------------- 50 | 51 | # store full metadata.txt as dict into a var 52 | __plugin_md__ = plugin_metadata_as_dict() 53 | 54 | __author__ = __plugin_md__.get("general").get("author") 55 | __copyright__ = "2014 - {0}, {1}".format(date.today().year, __author__) 56 | __email__ = __plugin_md__.get("general").get("email") 57 | __icon_path__ = DIR_PLUGIN_ROOT.resolve() / __plugin_md__.get("general").get("icon") 58 | __keywords__ = __plugin_md__.get("general").get("repository").split("tags") 59 | __license__ = "GPL-2.0" 60 | __summary__ = "{}\n{}".format( 61 | __plugin_md__.get("general").get("description"), 62 | __plugin_md__.get("general").get("about"), 63 | ) 64 | 65 | __title__ = __plugin_md__.get("general").get("name") 66 | __title_clean__ = "".join(e for e in __title__ if e.isalnum()) 67 | __uri_homepage__ = __plugin_md__.get("general").get("homepage") 68 | __uri_repository__ = __plugin_md__.get("general").get("repository") 69 | __uri_tracker__ = __plugin_md__.get("general").get("tracker") 70 | __uri__ = __uri_repository__ 71 | 72 | __version__ = __plugin_md__.get("general").get("version") 73 | __version_info__ = tuple( 74 | [ 75 | int(num) if num.isdigit() else num 76 | for num in __version__.replace("-", ".", 1).split(".") 77 | ] 78 | ) 79 | 80 | # ############################################################################# 81 | # ##### Main ####################### 82 | # ################################## 83 | if __name__ == "__main__": 84 | plugin_md = plugin_metadata_as_dict() 85 | assert isinstance(plugin_md, dict) 86 | assert plugin_md.get("general").get("name") == __title__ 87 | print("Plugin: " + __title__) 88 | print("Plugin: " + __title_clean__) 89 | print("By: " + __author__) 90 | print("Version: " + __version__) 91 | print("Description: " + __summary__) 92 | print( 93 | "For: %s > QGIS > %s" 94 | % ( 95 | plugin_md.get("general").get("qgisminimumversion"), 96 | plugin_md.get("general").get("qgismaximumversion"), 97 | ) 98 | ) 99 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | /** 4 | * Copyright (C) 2016 BRGM (http://brgm.fr) 5 | * Copyright (C) 2016 Oslandia 6 | * Copyright (C) 2016 Camptocamp (http://camptocamp.com) 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Library General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Library General Public License for more details. 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, see . 19 | */ 20 | """ 21 | 22 | 23 | def classFactory(iface): 24 | from gml_application_schema_toolbox.main import GmlasPlugin 25 | 26 | return GmlasPlugin(iface) 27 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/constants.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | """ 4 | Plugin constants. 5 | """ 6 | 7 | # ############################################################################ 8 | # ########## Globals ############### 9 | # ################################## 10 | 11 | # TODO: add names variants of provider names 12 | DATABASE_TYPES = ( 13 | "postgres", 14 | "spatialite", 15 | ) 16 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/core/__init__.py -------------------------------------------------------------------------------- /gml_application_schema_toolbox/core/gml_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 BRGM (http:///brgm.fr) 2 | # Copyright (C) 2016 Oslandia 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Library General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 2 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Library General Public License for more details. 13 | # You should have received a copy of the GNU Library General Public 14 | # License along with this library; if not, see . 15 | 16 | 17 | import xml.etree.ElementTree as ET 18 | 19 | from gml_application_schema_toolbox.core.xml_utils import no_prefix 20 | 21 | 22 | def extract_features(doc): 23 | """Extract (Complex) features from a XML doc 24 | :param doc: a DOM document 25 | :returns: (bbox, bbox_srs, nodes) 26 | where bbox is a rectangle (xmin, ymin, xmax, ymax) representing a bounding box 27 | bbox_srs is the srsName of the bbox 28 | nodes is a list of nodes for each feature 29 | """ 30 | 31 | def _extract(node): 32 | features = [] 33 | bbox = None 34 | bbox_srs = None 35 | if node.tag.startswith("{http://www.opengis.net/wfs") and node.tag.endswith( 36 | "FeatureCollection" 37 | ): 38 | # WFS features 39 | for child in node: 40 | if no_prefix(child.tag) == "member": 41 | # a member may contain another featurecollection => recursive call 42 | for cchild in child: 43 | nbbox, nbbox_srs, nfeatures = _extract(cchild) 44 | if bbox is None: 45 | bbox = nbbox 46 | bbox_srs = nbbox_srs 47 | features += nfeatures 48 | elif no_prefix(child.tag) == "featureMembers": 49 | for cchild in child: 50 | features.append(cchild) 51 | elif no_prefix(child.tag) == "featureMember": 52 | for cchild in child: 53 | features.append(cchild) 54 | elif no_prefix(child.tag) == "boundedBy": 55 | lc = None 56 | uc = None 57 | for cchild in child: 58 | if no_prefix(cchild.tag) == "Envelope": 59 | for k, v in cchild.attrib.items(): 60 | if no_prefix(k) == "srsName": 61 | bbox_srs = v 62 | for ccchild in cchild: 63 | if no_prefix(ccchild.tag) == "lowerCorner": 64 | lc = ccchild.text 65 | elif no_prefix(ccchild.tag) == "upperCorner": 66 | uc = ccchild.text 67 | if lc is not None and uc is not None: 68 | lcp = [float(x) for x in lc.split(" ")] 69 | ucp = [float(x) for x in uc.split(" ")] 70 | bbox = (lcp[0], lcp[1], ucp[0], ucp[1]) 71 | 72 | elif node.tag.startswith("{http://www.opengis.net/sos/2") and node.tag.endswith( 73 | "GetObservationResponse" 74 | ): 75 | # SOS features 76 | for child in node: 77 | if no_prefix(child.tag) == "observationData": 78 | features.append(child[0]) 79 | else: 80 | # it seems to be an isolated feature 81 | features.append(node) 82 | return (bbox, bbox_srs, features) 83 | 84 | return _extract(doc.getroot()) 85 | 86 | 87 | def extract_features_from_file(file_path): 88 | return extract_features(ET.parse(file_path)) 89 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/core/proxy.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from osgeo import gdal 4 | from qgis.PyQt.QtCore import QSettings 5 | 6 | 7 | class qgis_proxy_settings: 8 | def __enter__(self): 9 | # keep previous config 10 | self.http_proxy = os.environ.get("http_proxy") 11 | self.https_proxy = os.environ.get("https_proxy") 12 | self.no_proxy = os.environ.get("no_proxy") 13 | self.gdal_http_proxy = gdal.GetConfigOption("GDAL_HTTP_PROXY") 14 | self.gdal_http_proxyuserpwd = gdal.GetConfigOption("GDAL_HTTP_PROXYUSERPWD") 15 | 16 | # apply QGIS proxy settings 17 | settings = QSettings() 18 | enabled = settings.value("proxy/proxyEnabled", "false").lower() == "true" 19 | proxytype = settings.value("proxy/proxyType", "") 20 | host = settings.value("proxy/proxyHost", "") 21 | port = settings.value("proxy/proxyPort", "") 22 | user = settings.value("proxy/proxyUser", "") 23 | password = settings.value("proxy/proxyPassword", "") 24 | excludes = settings.value("proxy/proxyExcludedUrls", "") 25 | if not excludes or (hasattr(excludes, "isNull") and excludes.isNull()): 26 | excludes = [] 27 | else: 28 | excludes = excludes.split("|") 29 | 30 | http_proxy = "" 31 | no_proxy = "" 32 | gdal_http_proxy = "" 33 | gdal_http_proxyuserpwd = "" 34 | 35 | if enabled: 36 | if proxytype == "HttpProxy": 37 | credentials = "" 38 | if user != "": 39 | credentials = "{}:{}@".format(user, password) 40 | http_proxy = "http://{}{}:{}".format(credentials, host, port) 41 | no_proxy = ",".join(excludes) 42 | 43 | os.environ["http_proxy"] = http_proxy 44 | os.environ["https_proxy"] = http_proxy 45 | os.environ["no_proxy"] = no_proxy 46 | 47 | gdal_http_proxy = "{}:{}".format(host, port) 48 | gdal.SetConfigOption("GDAL_HTTP_PROXY", gdal_http_proxy) 49 | if user != "": 50 | gdal_http_proxyuserpwd = "{}:{}".format(user, password) 51 | gdal.SetConfigOption("GDAL_HTTP_PROXYUSERPWD", gdal_http_proxyuserpwd) 52 | 53 | def __exit__(self, proxy_type, value, tb): 54 | # restore previous settings 55 | if self.http_proxy is not None: 56 | os.environ["http_proxy"] = self.http_proxy 57 | os.environ["http_proxy"] = self.http_proxy 58 | else: 59 | os.environ.pop("http_proxy", None) 60 | os.environ.pop("https_proxy", None) 61 | if self.no_proxy is not None: 62 | os.environ["no_proxy"] = self.no_proxy 63 | else: 64 | os.environ.pop("no_proxy", None) 65 | gdal.SetConfigOption("GDAL_HTTP_PROXY", self.gdal_http_proxy) 66 | gdal.SetConfigOption("GDAL_HTTP_PROXYUSERPWD", self.gdal_http_proxyuserpwd) 67 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/core/qgis_urlopener.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | # ############################################################################ 4 | # ########## Imports ############### 5 | # ################################## 6 | 7 | # Standard library 8 | import logging 9 | from io import BytesIO 10 | from typing import Dict 11 | 12 | # project 13 | from gml_application_schema_toolbox.toolbelt import PlgLogger, PlgOptionsManager 14 | from gml_application_schema_toolbox.toolbelt.network_manager import ( 15 | NetworkAccessManager, 16 | RequestsException, 17 | ) 18 | 19 | # ############################################################################ 20 | # ########## Globals ############### 21 | # ################################## 22 | 23 | __network_manager = None 24 | logger = logging.getLogger(__name__) 25 | plg_logger = PlgLogger() 26 | plg_settings = PlgOptionsManager().get_plg_settings() 27 | 28 | # ############################################################################ 29 | # ########## Functions ############# 30 | # ################################## 31 | 32 | 33 | def remote_open_from_qgis( 34 | uri: str, 35 | headers: Dict[bytes, bytes] = { 36 | b"Accept": b"application/xml", 37 | b"Accept-Language": bytes(plg_settings.network_language, "utf8"), 38 | b"User-Agent": bytes(plg_settings.network_http_user_agent, "utf8"), 39 | }, 40 | ) -> BytesIO: 41 | """Opens a remote URL using Network Acess Manager. In fact, just a shortcut. 42 | 43 | :param uri: URI to request 44 | :type uri: str 45 | :param headers: HTTP headers. Defaults to: \ 46 | .. code-block:: python 47 | 48 | { 49 | b"Accept": b"application/xml", 50 | b"Accept-Language": bytes(settings.value("default_language", "fr"), "utf8"), 51 | b"User-Agent": bytes(settings.value("http_user_agent", __title__), "utf8") 52 | } 53 | :type headers: Dict[bytes, bytes], optional 54 | 55 | :return: response content as bytesarray or None if something went wrong 56 | :rtype: BytesIO 57 | """ 58 | nam = NetworkAccessManager() 59 | try: 60 | response, content = nam.request(url=uri, headers=headers) 61 | plg_logger.log(response.status_code) 62 | return BytesIO(content) 63 | except RequestsException as err: 64 | logger.error(err) 65 | plg_logger.log( 66 | message="Request to {} failed. Trace: {}".format(uri, err), 67 | log_level=2, 68 | push=1, 69 | ) 70 | return None 71 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/core/xml_utils.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | # Copyright (C) 2016 BRGM (http:///brgm.fr) 4 | # Copyright (C) 2016 Oslandia 5 | # 6 | # This library is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU Library General Public 8 | # License as published by the Free Software Foundation; either 9 | # version 2 of the License, or (at your option) any later version. 10 | # 11 | # This library is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # Library General Public License for more details. 15 | # You should have received a copy of the GNU Library General Public 16 | # License along with this library; if not, see . 17 | 18 | # ############################################################################ 19 | # ########## Imports ############### 20 | # ################################## 21 | 22 | # standard 23 | import io 24 | import logging 25 | import xml.etree.ElementTree as ET 26 | 27 | # project 28 | from gml_application_schema_toolbox.toolbelt.log_handler import PlgLogger 29 | 30 | # ############################################################################ 31 | # ########## Globals ############### 32 | # ################################## 33 | 34 | logger = logging.getLogger(__name__) 35 | plg_logger = PlgLogger() 36 | 37 | # ############################################################################ 38 | # ########## Functions ############# 39 | # ################################## 40 | 41 | 42 | def no_prefix(tag): 43 | """Remove the namespace prefix from the given name""" 44 | if tag.startswith("{"): 45 | return tag[tag.rfind("}") + 1 :] 46 | return tag 47 | 48 | 49 | def prefix(tag): 50 | """Return the namespace prefix from the given name""" 51 | if tag.startswith("{"): 52 | return tag[1 : tag.rfind("}")] 53 | return "" 54 | 55 | 56 | def no_ns(s): 57 | """Remove namespace prefix, except on attributes""" 58 | i = s.find(":") 59 | if i != -1 and "@" not in s[:i]: 60 | return s[i + 1 :] 61 | return s 62 | 63 | 64 | def split_tag(tag): 65 | """Return a pair (ns prefix, tag) from a tag name""" 66 | if tag.startswith("{"): 67 | i = tag.rfind("}") 68 | return (tag[1:i], tag[i + 1 :]) 69 | return ("", tag) 70 | 71 | 72 | def remove_prefix(node): 73 | node.tag = no_prefix(node.tag) 74 | n = {} 75 | for k, v in node.attrib.items(): 76 | n[no_prefix(k)] = v 77 | node.attrib = n 78 | for child in node: 79 | remove_prefix(child) 80 | 81 | 82 | def resolve_xpath(node, xpath, ns_map=None): 83 | get_text = xpath.endswith("/text()") 84 | if get_text: 85 | xpath = xpath[0:-7] 86 | nodes = node.findall(xpath, ns_map) 87 | if get_text: 88 | nodes = [ 89 | node.text if node is not None and node.text is not None else "" 90 | for node in nodes 91 | ] 92 | if len(nodes) == 0: 93 | return None if not get_text else "" 94 | elif len(nodes) == 1: 95 | return nodes[0] 96 | else: 97 | return nodes 98 | 99 | 100 | def xml_root_tag(xml_file): 101 | """ 102 | Return the root tag of an XML file 103 | :param xml_file: the input XML file 104 | :returns: root tag, as a string 105 | """ 106 | for event, elem in ET.iterparse(xml_file, ["start"]): 107 | return elem.tag 108 | 109 | 110 | def xml_parse(xml_file): 111 | """ 112 | Parse an XML file, returns a tree of nodes and a dict of namespaces 113 | :param xml_file: the input XML file 114 | :returns: (doc, ns_map) 115 | """ 116 | root = None 117 | ns_map = {} # prefix -> ns_uri 118 | for event, elem in ET.iterparse(xml_file, ["start-ns", "start", "end"]): 119 | if event == "start-ns": 120 | ns_map[elem[0]] = elem[1] 121 | elif event == "start": 122 | if root is None: 123 | root = elem 124 | for prefix, uri in ns_map.items(): 125 | ET.register_namespace(prefix, uri) 126 | 127 | return (ET.ElementTree(root), ns_map) 128 | 129 | 130 | def xml_parse_from_string(s): 131 | return xml_parse(io.StringIO(s)) 132 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Redirecting... 7 | 8 | 9 | 10 | 11 | 12 |

Redirection to the online documentation...

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/extlibs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/extlibs/__init__.py -------------------------------------------------------------------------------- /gml_application_schema_toolbox/extlibs/owslib_hacks.py: -------------------------------------------------------------------------------- 1 | try: 2 | from urllib import urlencode 3 | except ImportError: 4 | from urllib.parse import urlencode 5 | 6 | from owslib.feature.wfs200 import WebFeatureService_2_0_0 7 | 8 | 9 | def getGETGetFeatureRequest_2_0_0( 10 | self, 11 | typename=None, 12 | filter=None, 13 | bbox=None, 14 | featureid=None, 15 | featureversion=None, 16 | propertyname=None, 17 | maxfeatures=None, 18 | storedQueryID=None, 19 | storedQueryParams=None, 20 | outputFormat=None, 21 | method="Get", 22 | startindex=None, 23 | sortby=None, 24 | ): 25 | storedQueryParams = storedQueryParams or {} 26 | 27 | base_url = next( 28 | ( 29 | m.get("url") 30 | for m in self.getOperationByName("GetFeature").methods 31 | if m.get("type").lower() == method.lower() 32 | ) 33 | ) 34 | base_url = base_url if base_url.endswith("?") else base_url + "?" 35 | 36 | request = {"service": "WFS", "version": self.version, "request": "GetFeature"} 37 | 38 | # check featureid 39 | if featureid: 40 | request["featureid"] = ",".join(featureid) 41 | elif bbox: 42 | request["bbox"] = self.getBBOXKVP(bbox, typename) 43 | elif filter: 44 | request["query"] = str(filter) 45 | if typename: 46 | typename = [typename] if isinstance(typename, str) else typename 47 | if int(self.version.split(".")[0]) >= 2: 48 | request["typenames"] = ",".join(typename) 49 | else: 50 | request["typename"] = ",".join(typename) 51 | if propertyname: 52 | request["propertyname"] = ",".join(propertyname) 53 | if sortby: 54 | request["sortby"] = ",".join(sortby) 55 | if featureversion: 56 | request["featureversion"] = str(featureversion) 57 | if maxfeatures: 58 | if int(self.version.split(".")[0]) >= 2: 59 | request["count"] = str(maxfeatures) 60 | else: 61 | request["maxfeatures"] = str(maxfeatures) 62 | if startindex: 63 | request["startindex"] = str(startindex) 64 | if storedQueryID: 65 | request["storedQuery_id"] = str(storedQueryID) 66 | for param in storedQueryParams: 67 | request[param] = storedQueryParams[param] 68 | if outputFormat is not None: 69 | request["outputFormat"] = outputFormat 70 | 71 | data = urlencode(request, doseq=True) 72 | 73 | return base_url + data 74 | 75 | 76 | WebFeatureService_2_0_0.getGETGetFeatureRequest = getGETGetFeatureRequest_2_0_0 77 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/__init__.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtWidgets import QMessageBox 2 | 3 | from gml_application_schema_toolbox.__about__ import __title__ 4 | 5 | 6 | class InputError(Exception): 7 | def __init__(self, message=None, parent=None): 8 | self.message = message 9 | self.parent = parent 10 | 11 | def show(self): 12 | if self.message is None: 13 | return 14 | QMessageBox.warning(self.parent, __title__, self.message) 15 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/bbox_widget.py: -------------------------------------------------------------------------------- 1 | """ 2 | Inspired by processing.gui.ExtentSelectionPanel 3 | 4 | Note that this depends on some processing plugin classes 5 | """ 6 | 7 | import os 8 | 9 | from processing.gui.RectangleMapTool import RectangleMapTool 10 | from qgis.core import QgsCoordinateReferenceSystem, QgsProject, QgsRectangle 11 | from qgis.PyQt import uic 12 | from qgis.PyQt.QtGui import QCursor 13 | from qgis.PyQt.QtWidgets import QAction, QInputDialog, QMenu 14 | from qgis.utils import iface 15 | 16 | WIDGET, BASE = uic.loadUiType( 17 | os.path.join(os.path.dirname(__file__), "..", "ui", "bbox_widget.ui") 18 | ) 19 | 20 | 21 | class BboxWidget(BASE, WIDGET): 22 | def __init__(self, parent=None): 23 | super(BboxWidget, self).__init__(parent) 24 | self.setupUi(self) 25 | 26 | self.dialog = None 27 | 28 | self.btnSelect.clicked.connect(self.selectExtent) 29 | 30 | canvas = iface.mapCanvas() 31 | self.prevMapTool = canvas.mapTool() 32 | self.tool = RectangleMapTool(canvas) 33 | self.tool.rectangleCreated.connect(self.updateExtent) 34 | 35 | def selectExtent(self): 36 | popupmenu = QMenu() 37 | useLayerExtentAction = QAction( 38 | self.tr("Use layer/canvas extent"), self.btnSelect 39 | ) 40 | selectOnCanvasAction = QAction( 41 | self.tr("Select extent on canvas"), self.btnSelect 42 | ) 43 | 44 | popupmenu.addAction(useLayerExtentAction) 45 | popupmenu.addAction(selectOnCanvasAction) 46 | 47 | selectOnCanvasAction.triggered.connect(self.selectOnCanvas) 48 | useLayerExtentAction.triggered.connect(self.useLayerExtent) 49 | 50 | popupmenu.exec_(QCursor.pos()) 51 | 52 | def useLayerExtent(self): 53 | CANVAS_KEY = "Use canvas extent" 54 | extentsDict = {} 55 | extentsDict[CANVAS_KEY] = { 56 | "extent": iface.mapCanvas().extent(), 57 | "authid": iface.mapCanvas().mapSettings().destinationCrs().authid(), 58 | } 59 | extents = [CANVAS_KEY] 60 | for layer in QgsProject.instance().mapLayers().values(): 61 | authid = layer.crs().authid() 62 | layerName = layer.name() 63 | extents.append(layerName) 64 | extentsDict[layerName] = {"extent": layer.extent(), "authid": authid} 65 | (item, ok) = QInputDialog.getItem( 66 | self, self.tr("Select extent"), self.tr("Use extent from"), extents, False 67 | ) 68 | if ok: 69 | self.setValue(extentsDict[item]["extent"], extentsDict[item]["authid"]) 70 | 71 | def selectOnCanvas(self): 72 | canvas = iface.mapCanvas() 73 | canvas.setMapTool(self.tool) 74 | if self.dialog: 75 | self.dialog.showMinimized() 76 | 77 | def updateExtent(self): 78 | self.setValue( 79 | self.tool.rectangle(), 80 | iface.mapCanvas().mapSettings().destinationCrs().authid(), 81 | ) 82 | 83 | self.tool.reset() 84 | canvas = iface.mapCanvas() 85 | canvas.setMapTool(self.prevMapTool) 86 | if self.dialog: 87 | self.dialog.showNormal() 88 | self.dialog.raise_() 89 | self.dialog.activateWindow() 90 | 91 | def setValue(self, value, crs_authid): 92 | if isinstance(value, QgsRectangle): 93 | s = "{},{},{},{}".format( 94 | value.xMinimum(), value.yMinimum(), value.xMaximum(), value.yMaximum() 95 | ) 96 | elif isinstance(value, str): 97 | s = value 98 | else: 99 | s = ",".join([str(v) for v in value]) 100 | 101 | s = "{},{}".format(s, crs_authid) 102 | 103 | self.leText.setText(s) 104 | return True 105 | 106 | def value(self): 107 | return self.leText.text() 108 | 109 | def rectangle(self): 110 | if self.value() == "": 111 | return None 112 | xmin, ymin, xmax, ymax = [float(x) for x in self.value().split(",")[0:4]] 113 | return QgsRectangle(xmin, ymin, xmax, ymax) 114 | 115 | def crs(self): 116 | if self.value() == "": 117 | return None 118 | return QgsCoordinateReferenceSystem(self.value().split(",")[4]) 119 | 120 | def isValid(self): 121 | try: 122 | crs = self.crs() 123 | assert crs.isValid() 124 | return True 125 | except Exception: 126 | return False 127 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/custom_viewers.py: -------------------------------------------------------------------------------- 1 | """ 2 | /** 3 | * Copyright (C) 2016 BRGM (http:///brgm.fr) 4 | * Copyright (C) 2016 Oslandia 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Library General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Library General Public License for more details. 15 | * You should have received a copy of the GNU Library General Public 16 | * License along with this library; if not, see . 17 | */ 18 | """ 19 | 20 | # standard library 21 | import sys 22 | 23 | # plugin 24 | from gml_application_schema_toolbox.viewers import ( # noqa: F401 25 | GeologyLogViewer, 26 | WML2TimeSeriesViewer, 27 | ) 28 | 29 | __custom_viewers = None 30 | 31 | 32 | def get_custom_viewers(): 33 | global __custom_viewers 34 | 35 | if __custom_viewers is not None: 36 | # viewers already loaded 37 | return __custom_viewers 38 | __custom_viewers = {} 39 | 40 | # introspect the viewers module 41 | module = sys.modules["gml_application_schema_toolbox.viewers"] 42 | for klass in dir(module): 43 | if klass.startswith("__"): 44 | continue 45 | k = getattr(module, klass) 46 | if hasattr(k, "xml_tag"): 47 | r = k.xml_tag() 48 | fltr = None 49 | if isinstance(r, tuple): 50 | tag, fltr = r 51 | else: 52 | tag = r 53 | __custom_viewers[tag] = (k, fltr) 54 | 55 | return __custom_viewers 56 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/gmlas_panel_mixin.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | """ 4 | /*************************************************************************** 5 | GmlasPanelMixin 6 | A QGIS plugin 7 | GMLAS Plugin 8 | ------------------- 9 | begin : 2016-12-09 10 | git sha : $Format:%H$ 11 | copyright : (C) 2016 by Arnaud Morvan - www.camptocamp.com 12 | email : arnaud.morvan@camptocamp.com 13 | ***************************************************************************/ 14 | 15 | /*************************************************************************** 16 | * * 17 | * This program is free software; you can redistribute it and/or modify * 18 | * it under the terms of the GNU General Public License as published by * 19 | * the Free Software Foundation; either version 2 of the License, or * 20 | * (at your option) any later version. * 21 | * * 22 | ***************************************************************************/ 23 | """ 24 | # PyQGIS 25 | import processing 26 | 27 | # 3rd party 28 | from osgeo import gdal 29 | from qgis.core import Qgis, QgsApplication, QgsProcessingFeedback 30 | from qgis.PyQt.QtCore import QEventLoop, Qt, pyqtSlot 31 | from qgis.PyQt.QtWidgets import QFileDialog, QProgressDialog 32 | 33 | # project package 34 | from gml_application_schema_toolbox.__about__ import __title__ 35 | from gml_application_schema_toolbox.core.proxy import qgis_proxy_settings 36 | from gml_application_schema_toolbox.toolbelt.log_handler import PlgLogger 37 | 38 | # ############################################################################ 39 | # ########## Classes ############### 40 | # ################################## 41 | 42 | 43 | class GmlasPanelMixin: 44 | def __init__(self): 45 | # map to the plugin log handler 46 | self.plg_logger = PlgLogger() 47 | 48 | @pyqtSlot() 49 | def on_gmlasConfigButton_clicked(self): 50 | filepath, suffix_filter = QFileDialog.getOpenFileName( 51 | parent=self, 52 | caption=self.tr("Open GMLAS config file"), 53 | directory=self.gmlasConfigLineEdit.text(), 54 | filter=self.tr("XML Files (*.xml)"), 55 | ) 56 | if filepath: 57 | self.gmlasConfigLineEdit.setText(filepath) 58 | 59 | def translate(self, params): 60 | if params is None: 61 | return 62 | params["callback"] = self.translate_callback 63 | 64 | dlg = QProgressDialog(self) 65 | dlg.setWindowTitle(__title__) 66 | dlg.setLabelText("Operation in progress") 67 | dlg.setMinimum(0) 68 | dlg.setMaximum(100) 69 | dlg.setWindowModality(Qt.WindowModal) 70 | self.progress_dlg = dlg 71 | 72 | self.setCursor(Qt.WaitCursor) 73 | try: 74 | self.plg_logger.log("gdal.VectorTranslate({})".format(str(params))) 75 | gdal.PushErrorHandler(self.plg_logger.gdal_error_handler) 76 | with qgis_proxy_settings(): 77 | res = gdal.VectorTranslate(**params) 78 | gdal.PopErrorHandler() 79 | self.plg_logger.log(str(res)) 80 | finally: 81 | self.unsetCursor() 82 | self.progress_dlg.reset() 83 | self.progress_dlg = None 84 | 85 | def translate_callback(self, pct, msg, user_data): 86 | self.progress_dlg.setValue(int(100 * pct)) 87 | QgsApplication.processEvents(QEventLoop.ExcludeUserInputEvents) 88 | if self.progress_dlg.wasCanceled(): 89 | return 0 90 | return 1 91 | 92 | def translate_processing(self, params): 93 | """Use GDAL processing to convert GMLAS to database and vice versa. 94 | 95 | :param params: Parameters for GDAL processing 96 | :type params: dict 97 | """ 98 | feedback = QgsProcessingFeedback() 99 | if Qgis.versionInt() < 32400: 100 | self.log( 101 | message=f"gmlas:convertformat_gmlas with params = {params}", log_level=4 102 | ) 103 | res = processing.run("gmlas:convertformat_gmlas", params, feedback=feedback) 104 | else: 105 | self.log(message=f"gdal:convertformat with params = {params}", log_level=4) 106 | res = processing.run("gdal:convertformat", params, feedback=feedback) 107 | self.log(message=str(res), log_level=4) 108 | self.log(message=feedback.textLog(), log_level=4) 109 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/load_wizard_xml.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | # ############################################################################ 4 | # ########## Imports ############### 5 | # ################################## 6 | 7 | # Standard library 8 | import os 9 | 10 | from qgis.core import QgsEditorWidgetSetup, QgsProject 11 | from qgis.PyQt import uic 12 | from qgis.PyQt.QtCore import QRegExp, QVariant, pyqtSlot 13 | from qgis.PyQt.QtGui import QRegExpValidator 14 | from qgis.PyQt.QtWidgets import QComboBox, QLineEdit, QTableWidgetItem, QWizardPage 15 | 16 | from gml_application_schema_toolbox.core.load_gml_as_xml import load_as_xml_layer 17 | from gml_application_schema_toolbox.gui import qgis_form_custom_widget 18 | from gml_application_schema_toolbox.gui.progress_bar import ProgressBarLogger 19 | from gml_application_schema_toolbox.toolbelt.log_handler import PlgLogger 20 | 21 | # ############################################################################ 22 | # ########## Globals ############### 23 | # ################################## 24 | 25 | PAGE_3_W, _ = uic.loadUiType( 26 | os.path.join(os.path.dirname(__file__), "..", "ui", "load_wizard_xml_options.ui") 27 | ) 28 | 29 | 30 | # ############################################################################ 31 | # ########## Classes ############### 32 | # ################################## 33 | 34 | 35 | class LoadWizardXML(QWizardPage, PAGE_3_W): 36 | def __init__(self, parent=None): 37 | super().__init__(parent) 38 | self.log = PlgLogger().log 39 | self.setupUi(self) 40 | self.setFinalPage(True) 41 | 42 | self.attributeTable.selectionModel().selectionChanged.connect( 43 | self.onSelectMapping 44 | ) 45 | self.geometryColumnCheck.stateChanged.connect( 46 | self.geometryColumnEdit.setEnabled 47 | ) 48 | if __debug__: 49 | self.log(message=f"DEBUG {__name__} loaded.", log_level=5) 50 | 51 | def nextId(self): 52 | return -1 53 | 54 | def validatePage(self): 55 | gml_path = self.wizard().gml_path() 56 | 57 | # get attribute mapping 58 | mapping = {} 59 | for i in range(self.attributeTable.rowCount()): 60 | attr = self.attributeTable.cellWidget(i, 0).text() 61 | xpath = self.attributeTable.item(i, 2).text() 62 | combo = self.attributeTable.cellWidget(i, 1) 63 | attr_type = combo.itemData(combo.currentIndex()) 64 | mapping[attr] = (xpath, attr_type) 65 | 66 | # get geometry mapping 67 | gmapping = None 68 | if self.geometryColumnCheck.isChecked() and self.geometryColumnEdit.text(): 69 | gmapping = self.geometryColumnEdit.text() 70 | 71 | # add a progress bar during import 72 | lyrs = load_as_xml_layer( 73 | gml_path, 74 | is_remote=gml_path.startswith("http://") or gml_path.startswith("https://"), 75 | attributes=mapping, 76 | geometry_mapping=gmapping, 77 | logger=ProgressBarLogger("Importing features ..."), 78 | swap_xy=self.swapXYCheck.isChecked(), 79 | ) 80 | 81 | for lyr in lyrs.values(): 82 | # install an XML tree widget 83 | qgis_form_custom_widget.install_xml_tree_on_feature_form(lyr) 84 | 85 | # id column 86 | lyr.setEditorWidgetSetup(0, QgsEditorWidgetSetup("Hidden", {})) 87 | # _xml_ column 88 | lyr.setEditorWidgetSetup(2, QgsEditorWidgetSetup("XML", {})) 89 | lyr.setDisplayExpression("fid") 90 | 91 | QgsProject.instance().addMapLayers(lyrs.values()) 92 | 93 | return True 94 | 95 | @pyqtSlot() 96 | def on_addMappingBtn_clicked(self): 97 | lastRow = self.attributeTable.rowCount() 98 | self.attributeTable.insertRow(lastRow) 99 | combo = QComboBox(self.attributeTable) 100 | combo.addItem("String", QVariant.String) 101 | combo.addItem("Integer", QVariant.Int) 102 | combo.addItem("Real", QVariant.Double) 103 | combo.addItem("Date/Time", QVariant.DateTime) 104 | self.attributeTable.setCellWidget(lastRow, 1, combo) 105 | 106 | lineEdit = QLineEdit(self.attributeTable) 107 | # exclude id, fid and _xml from allowed field names 108 | lineEdit.setValidator(QRegExpValidator(QRegExp("(?!(id|fid|_xml_)).*"))) 109 | self.attributeTable.setCellWidget(lastRow, 0, lineEdit) 110 | 111 | self.attributeTable.setItem(lastRow, 2, QTableWidgetItem()) 112 | 113 | @pyqtSlot() 114 | def on_removeMappingBtn_clicked(self): 115 | idx = self.attributeTable.currentIndex() 116 | self.attributeTable.removeRow(idx.row()) 117 | 118 | def onSelectMapping(self, selected, deselected): 119 | self.removeMappingBtn.setEnabled(selected != -1) 120 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/progress_bar.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 BRGM (http:///brgm.fr) 2 | # Copyright (C) 2016 Oslandia 3 | # 4 | # This library is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU Library General Public 6 | # License as published by the Free Software Foundation; either 7 | # version 2 of the License, or (at your option) any later version. 8 | # 9 | # This library is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | # Library General Public License for more details. 13 | # You should have received a copy of the GNU Library General Public 14 | # License along with this library; if not, see . 15 | 16 | from qgis.PyQt.QtCore import QCoreApplication 17 | from qgis.PyQt.QtWidgets import QDialog, QLabel, QProgressBar, QVBoxLayout 18 | 19 | 20 | class ProgressBarLogger(QDialog): 21 | """A simple dialog with a progress bar and a label""" 22 | 23 | def __init__(self, title=None): 24 | QDialog.__init__(self, None) 25 | if title is not None: 26 | self.setWindowTitle(title) 27 | self.__label = QLabel(self) 28 | self.__layout = QVBoxLayout() 29 | self.__layout.addWidget(self.__label) 30 | self.__progress = QProgressBar(self) 31 | self.__layout.addWidget(self.__progress) 32 | self.setLayout(self.__layout) 33 | self.resize(600, 70) 34 | self.setFixedSize(600, 70) 35 | self.__progress.hide() 36 | self.show() 37 | 38 | def set_text(self, t): 39 | """Gets called when a text is to be logged""" 40 | if isinstance(t, tuple): 41 | lvl, msg = t 42 | else: 43 | msg = t 44 | self.__label.setText(msg) 45 | QCoreApplication.processEvents() 46 | 47 | def set_progress(self, i, n): 48 | """Gets called when there is a progression""" 49 | self.__progress.show() 50 | self.__progress.setMinimum(0) 51 | self.__progress.setMaximum(n) 52 | self.__progress.setValue(i) 53 | QCoreApplication.processEvents() 54 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/wait_cursor_context.py: -------------------------------------------------------------------------------- 1 | from qgis.PyQt.QtCore import Qt 2 | from qgis.PyQt.QtWidgets import QApplication 3 | 4 | 5 | class WaitCursor(object): 6 | def __enter__(self): 7 | QApplication.setOverrideCursor(Qt.WaitCursor) 8 | 9 | def __exit__(self, type, value, traceback): 10 | QApplication.restoreOverrideCursor() 11 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/xml_custom_widget.py: -------------------------------------------------------------------------------- 1 | from qgis.core import QgsFieldFormatter 2 | from qgis.gui import ( 3 | QgsEditorConfigWidget, 4 | QgsEditorWidgetFactory, 5 | QgsEditorWidgetWrapper, 6 | ) 7 | from qgis.PyQt.QtWidgets import QWidget 8 | 9 | 10 | class XMLCustomWidgetWrapper(QgsEditorWidgetWrapper): 11 | def __init__(self, vl, fieldIdx, editor, parent): 12 | super(XMLCustomWidgetWrapper, self).__init__(vl, fieldIdx, editor, parent) 13 | self._xml_widget = editor 14 | 15 | def createWidget(self, parent): 16 | self._xml_widget = QWidget(parent) 17 | self._xml_widget.hide() 18 | return self._xml_widget 19 | 20 | def value(self): 21 | return None 22 | 23 | def setValue(self, v): 24 | pass 25 | 26 | def initWidget(self, editor): 27 | pass 28 | 29 | def valid(self): 30 | return isinstance(self._xml_widget, QWidget) 31 | 32 | 33 | class XMLWidgetConfigDlg(QgsEditorConfigWidget): 34 | def __init__(self, vl, fieldIdx, parent): 35 | super(XMLWidgetConfigDlg, self).__init__(vl, fieldIdx, parent) 36 | 37 | def config(self): 38 | return {} 39 | 40 | def setConfig(self, cfg): 41 | pass 42 | 43 | 44 | class XMLWidgetFactory(QgsEditorWidgetFactory): 45 | def __init__(self): 46 | super(XMLWidgetFactory, self).__init__("XML") 47 | 48 | def create(self, vl, fieldIdx, editor, parent): 49 | # QgsVectorLayer* vl, int fieldIdx, QWidget* editor, QWidget* parent 50 | self.wrapper = XMLCustomWidgetWrapper(vl, fieldIdx, editor, parent) 51 | return self.wrapper 52 | 53 | def configWidget(self, vl, fieldIdx, parent): 54 | self.dlg = XMLWidgetConfigDlg(vl, fieldIdx, parent) 55 | return self.dlg 56 | 57 | 58 | class XMLWidgetFormatter(QgsFieldFormatter): 59 | def id(self): 60 | return "XML" 61 | 62 | def representValue(self, layer, fieldIdx, config, cache, value): 63 | return "<... XML data ...>" 64 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/gui/xml_dialog.py: -------------------------------------------------------------------------------- 1 | """ 2 | /*************************************************************************** 3 | CapabilitiesDialog 4 | A QGIS plugin 5 | GMLAS Plugin 6 | ------------------- 7 | begin : 2016-09-21 8 | git sha : $Format:%H$ 9 | copyright : (C) 2016 by Arnaud Morvan - www.camptocamp.com 10 | email : arnaud.morvan@camptocamp.com 11 | ***************************************************************************/ 12 | 13 | /*************************************************************************** 14 | * * 15 | * This program is free software; you can redistribute it and/or modify * 16 | * it under the terms of the GNU General Public License as published by * 17 | * the Free Software Foundation; either version 2 of the License, or * 18 | * (at your option) any later version. * 19 | * * 20 | ***************************************************************************/ 21 | """ 22 | 23 | import os 24 | 25 | from qgis.PyQt import uic 26 | from qgis.PyQt.QtGui import QStandardItem, QStandardItemModel 27 | from qgis.PyQt.QtXml import QDomDocument, QDomNode 28 | 29 | WIDGET, BASE = uic.loadUiType( 30 | os.path.join(os.path.dirname(__file__), "..", "ui", "xml_dialog.ui") 31 | ) 32 | 33 | 34 | class DomNodeItem(QStandardItem): 35 | def __init__(self, node): 36 | super(DomNodeItem, self).__init__() 37 | self._node = node 38 | self.setText(self.getText()) 39 | 40 | child = node.firstChild() 41 | while not child.isNull(): 42 | self.appendRow(DomNodeItem(child)) 43 | child = child.nextSibling() 44 | 45 | def getText(self): 46 | if self._node.isElement(): 47 | return "<{}>".format(self._node.nodeName()) 48 | 49 | if self._node.isText(): 50 | return self._node.nodeValue() 51 | 52 | if self._node.nodeType() == QDomNode.AttributeNode: 53 | return "attribute: {}: {}".format( 54 | self._node.nodeName(), self._node.nodeValue() 55 | ) 56 | 57 | if self._node.nodeValue(): 58 | return "{}: {}".format(self._node.nodeName(), self._node.nodeValue()) 59 | return self._node.nodeName() 60 | 61 | 62 | class DomDocumentModel(QStandardItemModel): 63 | def __init__(self, document, parent=None): 64 | super(DomDocumentModel, self).__init__(parent) 65 | self._document = document 66 | 67 | root = document.documentElement() 68 | child = root.firstChild() 69 | while not child.isNull(): 70 | self.appendRow(DomNodeItem(child)) 71 | child = child.nextSibling() 72 | 73 | 74 | class XmlDialog(BASE, WIDGET): 75 | def __init__(self, parent=None, xml=None): 76 | super(XmlDialog, self).__init__(parent) 77 | self.setupUi(self) 78 | 79 | document = QDomDocument() 80 | document.setContent(xml) 81 | model = DomDocumentModel(document) 82 | self.treeView.setModel(model) 83 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/metadata.txt: -------------------------------------------------------------------------------- 1 | # This file contains metadata for your plugin. Beginning 2 | # with version 1.8 this is the preferred way to supply information about a 3 | # plugin. The current method of embedding metadata in __init__.py will 4 | # be supported until version 2.0 5 | 6 | # This file should be included when you package your plugin. 7 | 8 | # Mandatory items: 9 | 10 | 11 | [general] 12 | name=QGIS GML Application Schema Toolbox 13 | qgisMinimumVersion=3.16 14 | qgisMaximumVersion=3.99 15 | description=Consumption and use of GML complex features like INSPIRE harmonised data (vector), GeoSciML within QGIS 16 | about=This plugins allows to import Complex Features streams in QGIS either through a native XML mode or through a conversion to a relational database model. 17 | hasProcessingProvider=yes 18 | 19 | version=1.4.0-beta6 20 | changelog= 21 | 22 | # tags are comma separated with spaces allowed 23 | tags=complex features, gml, gmlas, application schema, INSPIRE, GeoSciML, WaterML 24 | 25 | homepage=https://brgm.github.io/gml_application_schema_toolbox/ 26 | tracker=https://github.com/BRGM/gml_application_schema_toolbox/issues 27 | repository=https://github.com/BRGM/gml_application_schema_toolbox 28 | icon=resources/images/mActionAddGMLLayer.svg 29 | 30 | # experimental flag 31 | experimental=False 32 | 33 | # deprecated flag (applies to the whole plugin, not just a single version 34 | deprecated=False 35 | 36 | # Author contact information 37 | author=BRGM, Oslandia, Copernicus, Camptocamp, Spatialys, titellus 38 | 39 | # email of the plugin administrator 40 | email=qgis@oslandia.com 41 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/processing/__init__.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | from .provider import GmlasProvider # noqa: F401 3 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/processing/gdal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/processing/gdal/__init__.py -------------------------------------------------------------------------------- /gml_application_schema_toolbox/processing/provider.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | """ 4 | Processing provider module. 5 | """ 6 | 7 | # PyQGIS 8 | from qgis.core import QgsProcessingProvider 9 | from qgis.PyQt.QtCore import QCoreApplication 10 | from qgis.PyQt.QtGui import QIcon 11 | 12 | # project 13 | from gml_application_schema_toolbox.__about__ import __icon_path__, __version__ 14 | 15 | from .gdal.ogr2ogr_3_24 import ogr2ogr_3_24 16 | 17 | # ############################################################################ 18 | # ########## Classes ############### 19 | # ################################## 20 | 21 | 22 | class GmlasProvider(QgsProcessingProvider): 23 | """ 24 | Processing provider class. 25 | """ 26 | 27 | def loadAlgorithms(self): 28 | """Loads all algorithms belonging to this provider.""" 29 | self.addAlgorithm(ogr2ogr_3_24()) 30 | 31 | def id(self) -> str: 32 | """Unique provider id, used for identifying it. This string should be unique, \ 33 | short, character only string, eg "qgis" or "gdal". \ 34 | This string should not be localised. 35 | 36 | :return: provider ID 37 | :rtype: str 38 | """ 39 | return "gmlas" 40 | 41 | def name(self) -> str: 42 | """Returns the provider name, which is used to describe the provider 43 | within the GUI. This string should be short (e.g. "Lastools") and localised. 44 | 45 | :return: provider name 46 | :rtype: str 47 | """ 48 | return self.tr("GMLAS") 49 | 50 | def longName(self) -> str: 51 | """Longer version of the provider name, which can include 52 | extra details such as version numbers. E.g. "Lastools LIDAR tools". This string should be localised. The default 53 | implementation returns the same string as name(). 54 | 55 | :return: provider long name 56 | :rtype: str 57 | """ 58 | return self.tr("GML Application Schema - Toolbox") 59 | 60 | def icon(self) -> QIcon: 61 | """QIcon used for your provider inside the Processing toolbox menu. 62 | 63 | :return: provider icon 64 | :rtype: QIcon 65 | """ 66 | return QIcon(str(__icon_path__)) 67 | 68 | def tr(self, message: str) -> str: 69 | """Get the translation for a string using Qt translation API. 70 | 71 | :param message: String for translation. 72 | :type message: str, QString 73 | 74 | :returns: Translated version of message. 75 | :rtype: str 76 | """ 77 | # noinspection PyTypeChecker,PyArgumentList,PyCallByClass 78 | return QCoreApplication.translate(self.__class__.__name__, message) 79 | 80 | def versionInfo(self) -> str: 81 | """Version information for the provider, or an empty string if this is not \ 82 | applicable (e.g. for inbuilt Processing providers). For plugin based providers, \ 83 | this should return the plugin’s version identifier. 84 | 85 | :return: version 86 | :rtype: str 87 | """ 88 | return __version__ 89 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | resources/images/logo_brgm.svg 4 | resources/images/logo_c2c.svg 5 | resources/images/logo_eea.png 6 | resources/images/logo_oslandia.png 7 | resources/images/logo_spatialys.png 8 | resources/images/mActionAddGMLLayer.svg 9 | resources/images/mActionOpenTableGML.svg 10 | resources/images/mActionShowSchema.svg 11 | 12 | 13 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources/gui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/resources/gui/__init__.py -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources/images/info-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources/images/logo_eea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/resources/images/logo_eea.png -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources/images/logo_oslandia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/resources/images/logo_oslandia.png -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources/images/logo_spatialys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/gml_application_schema_toolbox/resources/images/logo_spatialys.png -------------------------------------------------------------------------------- /gml_application_schema_toolbox/resources/images/plot.svg: -------------------------------------------------------------------------------- 1 | 2 | 22 | 38 | 44 | 49 | 53 | 60 | 62 | 68 | 72 | 77 | 82 | 84 | 86 | 88 | 90 | image/svg+xml 93 | 96 | 99 | 101 | 104 | Openclipart 107 | 109 | 111 | 113 | 116 | 119 | 122 | 125 | 127 | 129 | 131 | 133 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/add_foreign_key_constraint.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "{schema}"."{foreign_key.table}" 2 | ADD CONSTRAINT "{foreign_key.name}" 3 | FOREIGN KEY ("{foreign_key.column}") 4 | REFERENCES "{schema}"."{foreign_key.referenced_table}" ("{foreign_key.referenced_column}"); 5 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/add_unique_constraint.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "{schema}"."{table}" 2 | ADD CONSTRAINT "{constraint}" 3 | UNIQUE ("{column}"); 4 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/constraint_exists.sql: -------------------------------------------------------------------------------- 1 | SELECT count(*) 2 | FROM information_schema.table_constraints 3 | WHERE table_schema = '{schema}' 4 | AND table_name = '{table}' 5 | AND constraint_name = '{constraint}'; 6 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/drop_constraint.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "{schema}"."{table}" 2 | DROP CONSTRAINT "{constraint}"; 3 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/foreign_key_many_to_many.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | _ogr_fields_metadata.field_junction_layer, 3 | _ogr_layer_relationships.parent_layer, 4 | _ogr_layer_relationships.parent_pkid, 5 | _ogr_layer_relationships.child_layer, 6 | _ogr_layer_relationships.child_pkid 7 | 8 | FROM "{schema}"._ogr_fields_metadata 9 | 10 | LEFT JOIN "{schema}"._ogr_layer_relationships 11 | ON _ogr_layer_relationships.parent_element_name = _ogr_fields_metadata.field_name 12 | AND _ogr_layer_relationships.parent_layer = _ogr_fields_metadata.layer_name 13 | 14 | -- Filter by existing columns in current schema 15 | INNER JOIN information_schema.tables 16 | ON tables.table_schema = '{schema}' 17 | AND tables.table_name = _ogr_fields_metadata.field_junction_layer 18 | 19 | WHERE field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'; 20 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/foreign_key_one_to_many.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | _ogr_fields_metadata.layer_name, 3 | _ogr_fields_metadata.field_name, 4 | _ogr_layer_relationships.child_layer, 5 | _ogr_layer_relationships.child_pkid 6 | 7 | FROM "{schema}"._ogr_fields_metadata 8 | 9 | INNER JOIN "{schema}"._ogr_layer_relationships 10 | ON _ogr_layer_relationships.parent_element_name = _ogr_fields_metadata.field_name 11 | AND _ogr_layer_relationships.parent_layer = _ogr_fields_metadata.layer_name 12 | AND _ogr_layer_relationships.child_layer = _ogr_fields_metadata.field_related_layer 13 | 14 | -- Filter by existing columns in current schema 15 | INNER JOIN information_schema.columns 16 | ON columns.table_schema = '{schema}' 17 | AND columns.table_name = _ogr_fields_metadata.layer_name 18 | AND columns.column_name = _ogr_fields_metadata.field_name 19 | 20 | INNER JOIN information_schema.columns referenced_columns 21 | ON referenced_columns.table_schema = '{schema}' 22 | AND referenced_columns.table_name = _ogr_layer_relationships.child_layer 23 | AND referenced_columns.column_name = _ogr_layer_relationships.child_pkid 24 | 25 | WHERE field_category IN ( 26 | 'PATH_TO_CHILD_ELEMENT_NO_LINK', 27 | 'PATH_TO_CHILD_ELEMENT_WITH_LINK'); 28 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/get_1_1_relations.sql: -------------------------------------------------------------------------------- 1 | select 2 | layer_name, field_name, field_related_layer, r.child_pkid 3 | from 4 | {schema}_ogr_fields_metadata f 5 | join {schema}_ogr_layer_relationships r 6 | on r.parent_layer = f.layer_name 7 | and r.parent_element_name = f.field_name 8 | where 9 | field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK') 10 | and field_max_occurs=1 11 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/sql/get_1_n_relations.sql: -------------------------------------------------------------------------------- 1 | select 2 | layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid 3 | from 4 | {schema}_ogr_fields_metadata f 5 | join {schema}_ogr_layer_relationships r 6 | on r.parent_layer = f.layer_name 7 | and r.child_layer = f.field_related_layer 8 | where 9 | field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK') 10 | and field_max_occurs>1 11 | -- junctions - 1st way 12 | union all 13 | select 14 | layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid 15 | from 16 | {schema}_ogr_fields_metadata f 17 | join {schema}_ogr_layer_relationships r 18 | on r.parent_layer = f.layer_name 19 | and r.child_layer = f.field_related_layer 20 | where 21 | field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE' 22 | -- junctions - 2nd way 23 | union all 24 | select 25 | field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid 26 | from 27 | {schema}_ogr_fields_metadata f 28 | join {schema}_ogr_layer_relationships r 29 | on r.parent_layer = f.layer_name 30 | and r.child_layer = f.field_related_layer 31 | where 32 | field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE' 33 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/toolbelt/__init__.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | from gml_application_schema_toolbox.toolbelt.log_handler import PlgLogger # noqa: F401 3 | from gml_application_schema_toolbox.toolbelt.preferences import ( # noqa: F401 4 | PlgOptionsManager, 5 | ) 6 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/toolbelt/file_downloader.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | """ 4 | Functions used to manage network requests (remote files, etc.) 5 | """ 6 | 7 | # Standard library 8 | import logging 9 | 10 | # PyQGIS 11 | from qgis.core import QgsFileDownloader 12 | from qgis.PyQt.QtCore import QEventLoop, QUrl 13 | 14 | # project 15 | from gml_application_schema_toolbox.toolbelt.log_handler import PlgLogger 16 | 17 | # ############################################################################ 18 | # ########## Globals ############### 19 | # ################################## 20 | 21 | logger = logging.getLogger(__name__) 22 | plg_logger = PlgLogger() 23 | 24 | # ############################################################################ 25 | # ########## Functions ############# 26 | # ################################## 27 | 28 | 29 | def get_from_http(uri: str, output_path: str) -> str: 30 | """Download a file from a remote web server accessible through HTTP. 31 | 32 | :param uri: web URL to the QGIS project 33 | :type uri: str 34 | :param output_path: path to the local file 35 | :type output_path: str 36 | 37 | :return: output path 38 | :rtype: str 39 | """ 40 | msg_log = f"Downloading file from {uri} to {output_path}" 41 | logger.debug(msg_log) 42 | plg_logger.log(msg_log) 43 | # download it 44 | loop = QEventLoop() 45 | project_download = QgsFileDownloader( 46 | url=QUrl(uri), outputFileName=output_path, delayStart=True 47 | ) 48 | project_download.downloadExited.connect(loop.quit) 49 | project_download.startDownload() 50 | loop.exec_() 51 | 52 | plg_logger.log(message=f"Download of {uri} succeedeed", log_level=3) 53 | return output_path 54 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/toolbelt/log_handler.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa: E265 2 | 3 | # standard library 4 | import logging 5 | 6 | # PyQGIS 7 | from qgis.core import QgsMessageLog 8 | from qgis.utils import iface 9 | 10 | # project package 11 | import gml_application_schema_toolbox.toolbelt.preferences as plg_prefs_hdlr 12 | from gml_application_schema_toolbox.__about__ import __title__ 13 | 14 | # ############################################################################ 15 | # ########## Classes ############### 16 | # ################################## 17 | 18 | 19 | class PlgLogger(logging.Handler): 20 | """Python logging handler supercharged with QGIS useful methods.""" 21 | 22 | @staticmethod 23 | def log( 24 | message: str, 25 | application: str = __title__, 26 | log_level: int = 0, 27 | push: bool = False, 28 | ): 29 | """Send messages to QGIS messages windows and to the user as a message bar. \ 30 | Plugin name is used as title. 31 | 32 | :param message: message to display 33 | :type message: str 34 | :param application: name of the application sending the message. \ 35 | Defaults to __about__.__title__ 36 | :type application: str, optional 37 | :param log_level: message level. Possible values: 0 (info), 1 (warning), \ 38 | 2 (critical), 3 (success), 4 (none - grey). Defaults to 0 (info) 39 | :type log_level: int, optional 40 | :param push: also display the message in the QGIS message bar in addition to \ 41 | the log, defaults to False 42 | :type push: bool, optional 43 | 44 | :Example: 45 | 46 | .. code-block:: python 47 | 48 | log(message="Plugin loaded - INFO", log_level=0, push=1) 49 | log(message="Plugin loaded - WARNING", log_level=1, push=1) 50 | log(message="Plugin loaded - ERROR", log_level=2, push=1) 51 | log(message="Plugin loaded - SUCCESS", log_level=3, push=1) 52 | log(message="Plugin loaded - TEST", log_level=4, push=1) 53 | """ 54 | # if debug mode, let's ignore INFO, SUCCESS and TEST 55 | debug_mode = plg_prefs_hdlr.PlgOptionsManager.get_plg_settings().debug_mode 56 | if not debug_mode and (1 < log_level < 3 or not push): 57 | return 58 | 59 | # ensure message is a string 60 | if not isinstance(message, str): 61 | try: 62 | message = str(message) 63 | except Exception as err: 64 | err_msg = "Log message must be a string, not: {}. Trace: {}".format( 65 | type(message), err 66 | ) 67 | logging.error(err_msg) 68 | message = err_msg 69 | 70 | # send it to QGIS messages panel 71 | QgsMessageLog.logMessage( 72 | message=message, tag=application, notifyUser=push, level=log_level 73 | ) 74 | 75 | # optionally, display message on QGIS Message bar (above the map canvas) 76 | if push: 77 | iface.messageBar().pushMessage( 78 | title=application, 79 | text=message, 80 | level=log_level, 81 | duration=(log_level + 1) * 3, 82 | ) 83 | 84 | def gdal_error_handler(self, eErrClass, err_no, msg: str): 85 | """Shortcut to log method specific to GDAL errors. 86 | 87 | :param eErrClass: [description] 88 | :type eErrClass: [type] 89 | :param err_no: [description] 90 | :type err_no: [type] 91 | :param msg: error message 92 | :type msg: str 93 | """ 94 | self.log( 95 | message=f"{eErrClass} {err_no}: {msg}".format(eErrClass, err_no, msg), 96 | log_level=2, 97 | ) 98 | 99 | def emit(self, record): 100 | try: 101 | msg = self.format(record) 102 | QgsMessageLog.logMessage(msg, self.tag) 103 | self.flush() 104 | except (KeyboardInterrupt, SystemExit): 105 | raise 106 | except Exception as err: 107 | QgsMessageLog.logMessage(err, self.tag, QgsMessageLog.ERROR) 108 | self.handleError(record) 109 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/ui/bbox_widget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 249 10 | 23 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 2 19 | 20 | 21 | 0 22 | 23 | 24 | 25 | 26 | 27 | 0 28 | 0 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 40 | 41 | 42 | ... 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/ui/load_wizard_data_source.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | WizardPage 4 | 5 | 6 | 7 | 0 8 | 0 9 | 398 10 | 126 11 | 12 | 13 | 14 | WizardPage 15 | 16 | 17 | Data source selection 18 | 19 | 20 | Please select the source of your data 21 | 22 | 23 | 24 | 25 | 26 | &WFS 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | File/&URL 39 | 40 | 41 | 42 | 43 | 44 | 45 | false 46 | 47 | 48 | 49 | 50 | 51 | 52 | false 53 | 54 | 55 | ... 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Qt::Vertical 65 | 66 | 67 | 68 | 20 69 | 40 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | sourceFromFile 80 | toggled(bool) 81 | gmlPathLineEdit 82 | setEnabled(bool) 83 | 84 | 85 | 48 86 | 48 87 | 88 | 89 | 224 90 | 48 91 | 92 | 93 | 94 | 95 | sourceFromFile 96 | toggled(bool) 97 | gmlPathButton 98 | setEnabled(bool) 99 | 100 | 101 | 48 102 | 48 103 | 104 | 105 | 374 106 | 48 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/ui/load_wizard_load.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | WizardPage 4 | 5 | 6 | 7 | 0 8 | 0 9 | 587 10 | 99 11 | 12 | 13 | 14 | WizardPage 15 | 16 | 17 | Loading options 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Load in XML &mode 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | Load in relational &mode (GMLAS) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Download to 46 | 47 | 48 | 49 | 50 | 51 | 52 | false 53 | 54 | 55 | <html><head/><body><p>The file downloaded will be saved in this location. If not set, a temporary file is created.</p></body></html> 56 | 57 | 58 | Create temporary file 59 | 60 | 61 | 62 | 63 | 64 | 65 | false 66 | 67 | 68 | ... 69 | 70 | 71 | 72 | 73 | 74 | 75 | false 76 | 77 | 78 | Download 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | donwloadCheck 90 | toggled(bool) 91 | downloadButton 92 | setEnabled(bool) 93 | 94 | 95 | 62 96 | 74 97 | 98 | 99 | 534 100 | 75 101 | 102 | 103 | 104 | 105 | donwloadCheck 106 | toggled(bool) 107 | outputPathButton 108 | setEnabled(bool) 109 | 110 | 111 | 62 112 | 74 113 | 114 | 115 | 472 116 | 74 117 | 118 | 119 | 120 | 121 | donwloadCheck 122 | toggled(bool) 123 | outputPathLineEdit 124 | setEnabled(bool) 125 | 126 | 127 | 62 128 | 74 129 | 130 | 131 | 287 132 | 74 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/ui/load_wizard_xml_options.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 601 10 | 364 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | XML options 18 | 19 | 20 | 21 | 22 | 23 | Swap X/Y axis 24 | 25 | 26 | 27 | 28 | 29 | 30 | Attribute mapping 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Add 40 | 41 | 42 | 43 | 44 | 45 | 46 | false 47 | 48 | 49 | Remove 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | QAbstractItemView::SelectRows 59 | 60 | 61 | true 62 | 63 | 64 | 65 | Attribute 66 | 67 | 68 | 69 | 70 | Type 71 | 72 | 73 | 74 | 75 | XPath 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Geometry column XPath 86 | 87 | 88 | 89 | 90 | 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/ui/xml_dialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CapabilitiesDialogBase 4 | 5 | 6 | 7 | 0 8 | 0 9 | 818 10 | 597 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | true 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 26 | false 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/viewers/README.md: -------------------------------------------------------------------------------- 1 | # Custom viewers API 2 | 3 | This directory contains the custom viewer plugins. 4 | 5 | A custom viewer plugin receives a complex feature in input and procudes a QWidget that offers a customized view of the feature's data. 6 | 7 | A first example is shipped with the current plugin: a custom viewer for timeseries of WaterML2 data. It can be found in [wml2_timeseries.py](wml2_timeseries.py) 8 | 9 | The API is subject to changes, but the current situation is the following. Any class found in this directory is a custom viewer if it has : 10 | 11 | - an `xml_tag()` method that gives the XML tag (with namespace) this widget is meant for 12 | - a `name()` method that gives its name 13 | - a `icon()` method that returns its icon, as QIcon 14 | - a `table_name()` method that gives the table name this widget is meant for in relational mode 15 | - a `init_from_xml()` method that returns a QWidget from a XML tree 16 | - a `init_from_model()` method that returns QWidget from a model, a sqlite connection and a feature id 17 | 18 | These two last methods should be merged in the future. Indeed, custom viewers could be made abstract enough to be independent from the mode used by the user to access data (relational or XML-based). 19 | Each custom viewer could then declare the data it is interested in by a list of XPath expressions. 20 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/viewers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | /** 3 | * Copyright (C) 2016 BRGM (http:///brgm.fr) 4 | * Copyright (C) 2016 Oslandia 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Library General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Library General Public License for more details. 15 | * You should have received a copy of the GNU Library General Public 16 | * License along with this library; if not, see . 17 | */ 18 | """ 19 | 20 | from gml_application_schema_toolbox.viewers.geology_logs import ( # noqa: F401 21 | GeologyLogViewer, 22 | ) 23 | from gml_application_schema_toolbox.viewers.wml2_timeseries import ( # noqa: F401 24 | WML2TimeSeriesViewer, 25 | ) 26 | -------------------------------------------------------------------------------- /gml_application_schema_toolbox/viewers/viewers_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | /** 3 | * Copyright (C) 2016 BRGM (http:///brgm.fr) 4 | * Copyright (C) 2016 Oslandia 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Library General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Library General Public License for more details. 15 | * You should have received a copy of the GNU Library General Public 16 | * License along with this library; if not, see . 17 | */ 18 | """ 19 | 20 | 21 | def resolve_xpath_in_model(model, table, xpath): 22 | # returns sql_tables, sql_wheres, sql_table, sql_column 23 | for column in table.columns(): 24 | if xpath.startswith(column.xpath()): 25 | return [table.name()], [], table.name(), column.name() 26 | 27 | for link in table.links(): 28 | link_xpath = link.xpath() 29 | if xpath.startswith(link_xpath): 30 | sql_tables, sql_wheres, sql_table, sql_column = resolve_xpath_in_model( 31 | model, link.ref_table(), xpath[len(link_xpath) + 1 :] 32 | ) 33 | sql_tables.append(table.name()) 34 | if link.max_occurs() is None: 35 | # * cardinality 36 | sql_wheres.append( 37 | ( 38 | link.ref_table().name() + "." + table.name() + "_id", 39 | table.name() + ".id", 40 | ) 41 | ) 42 | else: 43 | # 1 cardinality 44 | sql_wheres.append( 45 | ( 46 | link.ref_table().name() + ".id", 47 | table.name() + "." + link.name() + "_id", 48 | ) 49 | ) 50 | return sql_tables, sql_wheres, sql_table, sql_column 51 | return [], [], None, None 52 | -------------------------------------------------------------------------------- /requirements/development.txt: -------------------------------------------------------------------------------- 1 | # Develop matching the development guidelines 2 | # ------------------------------------------- 3 | 4 | black 5 | flake8>=3.8,<4.1 6 | flake8-builtins>=1.5,<1.6 7 | flake8-eradicate>=1.0,<1.5 8 | flake8-isort>=4.0,<4.3 9 | pre-commit>=2.12,<2.22 10 | -------------------------------------------------------------------------------- /requirements/documentation.txt: -------------------------------------------------------------------------------- 1 | # Documentation 2 | # ------------- 3 | 4 | ghp-import>=2.0,<2.2 5 | furo==2022.9.29 6 | myst-parser[linkify]>=0.15,<0.19 7 | sphinx-autobuild==2021.3.14 8 | sphinx-copybutton>=0.3,<1 9 | sphinxext-opengraph>=0.4,<1 10 | -------------------------------------------------------------------------------- /requirements/packaging.txt: -------------------------------------------------------------------------------- 1 | # Packaging 2 | # --------- 3 | 4 | qgis-plugin-ci>=2.0,<2.6 5 | -------------------------------------------------------------------------------- /requirements/testing.txt: -------------------------------------------------------------------------------- 1 | # Testing dependencies 2 | # -------------------- 3 | 4 | pytest-cov>=2.11,<3.1 5 | semver>=2.13,<2.14 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # -- Packaging -------------------------------------- 2 | [bdist_wheel] 3 | universal = 1 4 | 5 | [metadata] 6 | description-file = README.md 7 | 8 | [qgis-plugin-ci] 9 | plugin_path = gml_application_schema_toolbox 10 | github_organization_slug = BRGM 11 | project_slug = gml_application_schema_toolbox 12 | 13 | # -- Code quality ------------------------------------ 14 | [flake8] 15 | count = True 16 | exclude = 17 | # No need to traverse our git directory 18 | .git, 19 | # There's no value in checking cache directories 20 | __pycache__, 21 | # The conf file is mostly autogenerated, ignore it 22 | docs/conf.py, 23 | # The old directory contains Flake8 2.0 24 | old, 25 | # This contains our built documentation 26 | build, 27 | # This contains builds of flake8 that we don't want to check 28 | dist, 29 | # This contains local virtual environments 30 | .venv*, 31 | # do not watch on tests 32 | tests, 33 | # do not consider external packages 34 | */external/*, ext_libs/* 35 | ignore = ANN101, ANN102, D104, D400, D401, E121,E123,E126,E203,E226,E24,E704,W503,W504 36 | max-complexity = 15 37 | max-doc-length = 130 38 | max-line-length = 100 39 | output-file = dev_flake8_report.txt 40 | statistics = True 41 | tee = True 42 | 43 | [isort] 44 | ensure_newline_before_comments = True 45 | force_grid_wrap = 0 46 | include_trailing_comma = True 47 | line_length = 88 48 | multi_line_output = 3 49 | profile = black 50 | use_parentheses = True 51 | 52 | # -- Tests ---------------------------------------------- 53 | [tool:pytest] 54 | addopts = 55 | --junitxml=junit/test-results.xml 56 | --cov-config=setup.cfg 57 | --cov=gml_application_schema_toolbox 58 | --cov-report=html 59 | --cov-report=xml 60 | --ignore=tests/_wip/ 61 | norecursedirs = .* build dev development dist docs CVS fixtures _darcs {arch} *.egg venv _wip 62 | python_files = test_*.py 63 | testpaths = tests 64 | 65 | [coverage:run] 66 | branch = True 67 | include = 68 | gml_application_schema_toolbox/* 69 | omit = 70 | .venv/* 71 | *tests* 72 | 73 | [coverage:report] 74 | exclude_lines = 75 | if self.debug: 76 | pragma: no cover 77 | raise NotImplementedError 78 | if __name__ == .__main__.: 79 | 80 | ignore_errors = True 81 | show_missing = True 82 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/tests/__init__.py -------------------------------------------------------------------------------- /tests/basic_test_scenario/0_BoreholeView.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Borehole description 7 | http://ressource.brgm-rec.fr/data/BoreholeView/BSS001REWW 8 | Forage BSS001REWW 9 | http://ressource.brgm-rec.fr/data/Borehole/BSS001REWW 10 | 11 | 12 | 13 | BRGM (PIEZOMETRIE) 14 | INTRAFOR-COFOR 15 | 1974-11-30Z 16 | 17 | 18 | unknown 19 | 23.0 20 | 223.87 21 | http://www.opengis.net/def/crs/EPSG/0/5720 22 | http://ficheinfoterre.brgm.fr/InfoterreFiche/ficheBss.action?id=06512X0037/STREMY 23 | http://www.geocatalogue.fr/Detail.do?fileIdentifier=BR_BSS_BAA 24 | Not provided 25 | 26 | 27 | 46.1909541655103 5.18713262971692 28 | 29 | 30 | false 31 | false 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/dev/docker-compose_postgis.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | postgis: 5 | image: postgis/postgis:13-3.1-alpine 6 | ports: 7 | - "5454:5432" 8 | environment: 9 | - POSTGRES_DB=postgis_gmlas 10 | - POSTGRES_USER=plugin_gmlas 11 | - POSTGRES_PASSWORD=plugin_gmlas 12 | - POSTGRES_INITDB_ARGS="--data-checksums" 13 | -------------------------------------------------------------------------------- /tests/fixtures/geology_log1.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/tests/fixtures/geology_log1.sqlite -------------------------------------------------------------------------------- /tests/fixtures/timeseries1.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/tests/fixtures/timeseries1.sqlite -------------------------------------------------------------------------------- /tests/qgis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/tests/qgis/__init__.py -------------------------------------------------------------------------------- /tests/qgis/test_custom_viewers.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa E265 2 | 3 | """ 4 | Usage from the repo root folder: 5 | 6 | Launch it with something like 7 | 8 | `QGIS_DEBUG=0 QGIS_PREFIX_PATH=/home/hme/src/QGIS/build/output PYTHONPATH=/home/hme/src/QGIS/build/output/python python3 test_custom_viewers.py` 9 | 10 | .. code-block:: bash 11 | 12 | # for whole tests 13 | python -m unittest tests.test_custom_viewers 14 | # for specific test 15 | python -m unittest tests.test_custom_viewers.TestCustomViewers.test_custom_viewers_layer 16 | """ 17 | 18 | 19 | # PyQGIS 20 | from qgis.testing import unittest 21 | from sip import wrappertype 22 | 23 | # project 24 | from gml_application_schema_toolbox.gui.custom_viewers import get_custom_viewers 25 | 26 | 27 | # ############################################################################ 28 | # ########## Classes ############# 29 | # ################################ 30 | class TestCustomViewers(unittest.TestCase): 31 | """Tests""" 32 | 33 | def test_custom_viewers_loader(self): 34 | for viewer_cls, _ in get_custom_viewers().values(): 35 | self.assertIsInstance(viewer_cls, wrappertype) 36 | self.assertIsNone(_) 37 | 38 | 39 | # ############################################################################ 40 | # ####### Stand-alone run ######## 41 | # ################################ 42 | if __name__ == "__main__": 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /tests/qgis/test_gmlas_xpath.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa E265 2 | 3 | """ 4 | Usage from the repo root folder: 5 | 6 | .. code-block:: bash 7 | 8 | # for whole tests 9 | python -m unittest tests.test_gmlas_xpath 10 | # for specific test 11 | python -m unittest tests.test_gmlas_xpath.TestGMLASXPath.test_geologylog 12 | """ 13 | 14 | # standard library 15 | import unittest 16 | 17 | # project 18 | from gml_application_schema_toolbox.core.gmlas_xpath import GmlAsXPathResolver 19 | 20 | 21 | # ############################################################################ 22 | # ########## Classes ############# 23 | # ################################ 24 | class TestGMLASXPath(unittest.TestCase): 25 | def test_geologylog(self): 26 | resolver = GmlAsXPathResolver( 27 | "tests/fixtures/geology_log1.sqlite", "SQLite", "" 28 | ) 29 | v = resolver.resolve_xpath( 30 | "gw_geologylogcoverage", 31 | "id", 32 | "ab.ww.402557.log.1.coverage", 33 | "element/LogValue/value/DataRecord/field/Category/value/text()", 34 | ) 35 | self.assertEqual(sorted(v), ["Clay", "Gravel", "Soil", "Till"]) 36 | v = resolver.resolve_xpath( 37 | "gw_geologylogcoverage", 38 | "id", 39 | "ab.ww.402557.log.1.coverage", 40 | "element/LogValue/fromDepth/Quantity/value", 41 | ) 42 | self.assertEqual(sorted(v), [0.0, 0.3, 4.27, 9.14]) 43 | v = resolver.resolve_xpath( 44 | "gw_geologylogcoverage", 45 | "id", 46 | "ab.ww.402557.log.1.coverage", 47 | "element/LogValue/toDepth/Quantity/value", 48 | ) 49 | self.assertEqual(sorted(v), [0.3, 4.27, 9.14, 11.58]) 50 | 51 | def test_timeseries(self): 52 | resolver = GmlAsXPathResolver("tests/fixtures/timeseries1.sqlite", "SQLite", "") 53 | v = resolver.resolve_xpath( 54 | "measurementtimeseries", 55 | "id", 56 | "timeseries.927B7F661CE9CF9F3BF931A87E119E524A5B328F", 57 | "point/MeasurementTVP/time/text()", 58 | ) 59 | self.assertEqual(len(v), 5000) 60 | v = resolver.resolve_xpath( 61 | "measurementtimeseries", 62 | "id", 63 | "timeseries.927B7F661CE9CF9F3BF931A87E119E524A5B328F", 64 | "defaultPointMetadata/DefaultTVPMeasurementMetadata/interpolationType/@xlink:title", 65 | ) 66 | self.assertEqual(v, ["Instantaneous"]) 67 | v = resolver.resolve_xpath( 68 | "measurementtimeseries", 69 | "id", 70 | "timeseries.927B7F661CE9CF9F3BF931A87E119E524A5B328F", 71 | "defaultPointMetadata/DefaultTVPMeasurementMetadata/uom/@code", 72 | ) 73 | self.assertEqual(v, ["m"]) 74 | 75 | 76 | # ############################################################################ 77 | # ####### Stand-alone run ######## 78 | # ################################ 79 | if __name__ == "__main__": 80 | unittest.main() 81 | -------------------------------------------------------------------------------- /tests/qgis/test_load_as_xml.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa E265 2 | 3 | """ 4 | Usage from the repo root folder: 5 | 6 | Launch it with something like 7 | 8 | `QGIS_DEBUG=0 QGIS_PREFIX_PATH=/home/hme/src/QGIS/build/output PYTHONPATH=/home/hme/src/QGIS/build/output/python python3 test_load_as_xml.py` 9 | 10 | .. code-block:: bash 11 | 12 | # for whole tests 13 | python -m unittest tests.test_load_as_xml 14 | # for specific test 15 | python -m unittest tests.test_load_as_xml.TestLoadAsXML.test_load_as_xml_layer 16 | """ 17 | 18 | 19 | # standard library 20 | from pathlib import Path 21 | 22 | # PyQGIS 23 | from qgis.core import QgsVectorLayer 24 | from qgis.testing import start_app, unittest 25 | 26 | # project 27 | from gml_application_schema_toolbox.core.load_gml_as_xml import load_as_xml_layer 28 | 29 | # start_app() 30 | 31 | 32 | # ############################################################################ 33 | # ########## Classes ############# 34 | # ################################ 35 | class TestLoadAsXML(unittest.TestCase): 36 | def test_load_as_xml_layer(self): 37 | sample_file = Path("tests/fixtures/brgm_ef_piezo_50_2.xml") 38 | 39 | self.assertTrue(sample_file.is_file()) 40 | 41 | layer_gmloaded = load_as_xml_layer( 42 | xml_uri=str(sample_file.resolve()), 43 | is_remote=False, 44 | output_local_file="/tmp/gmlas_test_load_gml.gpkg", 45 | ) 46 | 47 | self.assertIsInstance(layer_gmloaded, dict) 48 | self.assertEqual(len(layer_gmloaded), 2) 49 | 50 | for layer in layer_gmloaded.values(): 51 | self.assertIsInstance(layer, QgsVectorLayer) 52 | self.assertEqual(layer.isValid(), True) 53 | self.assertEqual(layer.featureCount(), 50) 54 | 55 | 56 | # ############################################################################ 57 | # ####### Stand-alone run ######## 58 | # ################################ 59 | if __name__ == "__main__": 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /tests/qgis/test_plg_preferences.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa E265 2 | 3 | """ 4 | Usage from the repo root folder: 5 | 6 | .. code-block:: bash 7 | 8 | # for whole tests 9 | python -m unittest tests.test_plg_preferences 10 | # for specific test 11 | python -m unittest tests.test_plg_preferences.TestPlgPreferences.test_plg_preferences_structure 12 | """ 13 | 14 | # standard library 15 | import unittest 16 | 17 | # project 18 | from gml_application_schema_toolbox.__about__ import ( 19 | DIR_PLUGIN_ROOT, 20 | __title__, 21 | __version__, 22 | ) 23 | from gml_application_schema_toolbox.toolbelt.preferences import PlgSettingsStructure 24 | 25 | # ############################################################################ 26 | # ########## Classes ############# 27 | # ################################ 28 | 29 | 30 | class TestPlgPreferences(unittest.TestCase): 31 | def test_plg_preferences_structure(self): 32 | """Test version note named tuple structure and mechanisms.""" 33 | settings = PlgSettingsStructure() 34 | 35 | # global 36 | self.assertTrue(hasattr(settings, "debug_mode")) 37 | self.assertIsInstance(settings.debug_mode, bool) 38 | self.assertEqual(settings.debug_mode, False) 39 | 40 | self.assertTrue(hasattr(settings, "version")) 41 | self.assertIsInstance(settings.version, str) 42 | self.assertEqual(settings.version, __version__) 43 | 44 | # network 45 | self.assertTrue(hasattr(settings, "network_http_user_agent")) 46 | self.assertIsInstance(settings.network_http_user_agent, str) 47 | self.assertEqual(settings.network_http_user_agent, f"{__title__}/{__version__}") 48 | self.assertTrue(hasattr(settings, "network_language")) 49 | self.assertIsInstance(settings.network_language, str) 50 | self.assertEqual(settings.network_language, "en") 51 | self.assertTrue(hasattr(settings, "network_max_features")) 52 | self.assertIsInstance(settings.network_max_features, int) 53 | self.assertEqual(settings.network_max_features, 100) 54 | 55 | # usage 56 | self.assertTrue(hasattr(settings, "impex_access_mode")) 57 | self.assertIsInstance(settings.impex_access_mode, int) 58 | self.assertEqual(settings.impex_access_mode, 1) 59 | self.assertTrue(hasattr(settings, "impex_db_type")) 60 | self.assertIsInstance(settings.impex_db_type, int) 61 | self.assertEqual(settings.impex_db_type, 1) 62 | self.assertTrue(hasattr(settings, "impex_import_method")) 63 | self.assertIsInstance(settings.impex_import_method, int) 64 | self.assertEqual(settings.impex_import_method, 1) 65 | self.assertTrue(hasattr(settings, "impex_gmlas_config")) 66 | self.assertIsInstance(settings.impex_gmlas_config, str) 67 | self.assertEqual( 68 | settings.impex_gmlas_config, str(DIR_PLUGIN_ROOT / "conf" / "gmlasconf.xml") 69 | ) 70 | 71 | self.assertTrue(hasattr(settings, "last_file")) 72 | self.assertIsInstance(settings.last_file, (str, type(None))) 73 | self.assertIsNone(settings.last_file, None) 74 | 75 | self.assertTrue(hasattr(settings, "last_downloaded_file")) 76 | self.assertIsInstance(settings.last_downloaded_file, (str, type(None))) 77 | self.assertIsNone(settings.last_downloaded_file, None) 78 | 79 | self.assertTrue(hasattr(settings, "last_path")) 80 | self.assertIsInstance(settings.last_path, (str, type(None))) 81 | self.assertIsNone(settings.last_path, None) 82 | 83 | self.assertTrue(hasattr(settings, "last_downloaded_path")) 84 | self.assertIsInstance(settings.last_downloaded_path, (str, type(None))) 85 | self.assertIsNone(settings.last_downloaded_path, None) 86 | 87 | self.assertTrue(hasattr(settings, "last_source")) 88 | self.assertIsInstance(settings.last_source, str) 89 | self.assertEqual(settings.last_source, "file") 90 | 91 | 92 | # ############################################################################ 93 | # ####### Stand-alone run ######## 94 | # ################################ 95 | if __name__ == "__main__": 96 | unittest.main() 97 | -------------------------------------------------------------------------------- /tests/samples/BRGM_1GeolUnit_GeoSciML.xml: -------------------------------------------------------------------------------- 1 | 2 | dunes et cordons littorauxhttp://ressource.brgm-rec.fr/data/GeologicUnit/gu.gsmlb.1alwayshttp://resource.geosciml.org/classifier/cgi/valuequalifier/alwaysshoreline settinghttp://inspire.ec.europa.eu/codelist/EventEnvironmentValue/shorelineSetting50.0 95.050.095.05.0 50.05.050.0 -------------------------------------------------------------------------------- /tests/samples/BRGM_1GeolUnit_INSPIRE.xml: -------------------------------------------------------------------------------- 1 | 2 | dunes et cordons littorauxhttp://ressource.brgm-rec.fr/data/GeologicUnit/gu.ge.1gu.ge.1http://ressource.brgm-rec.fr/data/GeologicUnit/q350.0 95.05.0 50.0 -------------------------------------------------------------------------------- /tests/samples/BoreholeView_Instance.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Borehole numberBSS001QZJH from BSS (french subsoils databank) 6 | https://data.geoscience.fr/id/Borehole/BSS001QZJH 7 | Forage BSS001QZJH 8 | https://data.geoscience.fr/id/BoreholeView/BSS001QZJH 9 | 10 | 11 | 12 | Classified 13 | Classified 14 | 15 | 16 | unknown 17 | 631.0 18 | http://www.opengis.net/def/crs/EPSG/0/5720 19 | 25.0 20 | http://ficheinfoterre.brgm.fr/InfoterreFiche/ficheBss.action?id=BSS001QZJH 21 | http://www.opengis.net/def/nil/OGC/0/inapplicable 22 | https://data.geoscience.fr/id/dataset/borehole 23 | Not provided 24 | 25 | 26 | 2.76771 46.14524 27 | 28 | 29 | false 30 | false 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/samples/GW_Spring_GNS_uc1.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | Te Waikoropupu Springs ('Pupu Springs') 11 | 12 | 13 | 172.768,-40.849 14 | 15 | 16 | 17 | 18 | 3.7 20 | 22 | 24 | 25 | 26 | 27 | 29 | 31 | 32 | 33 | 35 | 36 | -------------------------------------------------------------------------------- /tests/samples/bigSizeFiles/ReadMe_testsFeedbacks_and_file_references.txt: -------------------------------------------------------------------------------- 1 | GMLAS Driver succesfully tested on 'some' big files 2 | 3 | ##### 4 | 1°/ Grand Lyon (100 MB) CityGML 1.0 5 | https://download.data.grandlyon.com/files/grandlyon/localisation/bati3d/LYON_1ER_2012.zip 6 | 7 | ##### 8 | 2°/ 3d geoinformation group - TU Delft - tests 9 | Stelios Vitalis during and after FOSS4G-E 2017 workshop 10 | https://3d.bk.tudelft.nl/svitalis/citygml/gdal/2017/07/24/messing-around-with-citygml-on-gdal-2.2.html 11 | 12 | Test on open datafiles created by 3d geoinformation group - TU Delft. 13 | 14 | Stelios Vitalis: """You may use the files as you want. You can also use this huge file (3,25 GB) which I have used with GDAL trunk and works great: 15 | https://www.dropbox.com/s/l3ssom0pfxy7a6x/3DMassing_2016_MTM3.gml?dl=0 16 | 17 | The driver scales to enormous sizes. Today, I was messing with a NYC dataset of 31GB (!) and the driver was working (ogr2ogr failed after a few hours when saving to PostGIS due 18 | to my disk getting full, but this is not GDAL's issue). 19 | """ 20 | 21 | Given 3DMassing_2016_MTM3.gml does not contain xsi:schemaLocation, the file has to be open explictly pointing to the following schemas. 22 | 23 | -oo XSD=http://schemas.opengis.net/citygml/landuse/2.0/landUse.xsd,http://schemas.opengis.net/citygml/cityfurniture/2.0/cityFurniture.xsd,http://schemas.opengis.net/citygml/texturedsurface/2.0/texturedSurface.xsd,http://schemas.opengis.net/citygml/transportation/2.0/transportation.xsd,http://schemas.opengis.net/citygml/building/2.0/building.xsd,http://schemas.opengis.net/citygml/waterbody/2.0/waterBody.xsd,http://schemas.opengis.net/citygml/relief/2.0/relief.xsd,http://schemas.opengis.net/citygml/vegetation/2.0/vegetation.xsd,http://schemas.opengis.net/citygml/cityobjectgroup/2.0/cityObjectGroup.xsd,http://schemas.opengis.net/citygml/generics/2.0/generics.xsd 24 | 25 | -------------------------------------------------------------------------------- /tests/samples/liste_flux.txt: -------------------------------------------------------------------------------- 1 | # Portrayal classes GeoSciML 2 | 3 | Ajouté au périmètre comme vu en réunion 4 | 5 | - En V4 : http://schemas.geosciml.org/geosciml/4.0/geosciml-portrayal.xsd 6 | 7 | - Pour l’instant nous n’avons pas d’exemple de flux en V4. Je vais voir pour en récupérer/générer en 2016. 8 | 9 | Exemples en V2. 10 | 11 | - Xsd V2 (/!\ gml 3.1.1) : http://schemas.geosciml.org/geosciml-portrayal/2.0/geosciml-portrayal.xsd 12 | 13 | - Flux V2 : http://auscope.dpi.nsw.gov.au/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&maxFeatures=5&typeName=gsmlp:BoreholeView 14 | 15 | ==> points plats 16 | 17 | 18 | 19 | # INSPIRE thème Environmental Monitoring Facilities (EF) 20 | 21 | - Xsd : http://inspire.ec.europa.eu/schemas/ef/4.0/EnvironmentalMonitoringFacilities.xsd 22 | 23 | - Instance : https://wfspoc.brgm-rec.fr/geoserver/ows?service=wfs&version=2.0.0&request=GetCapabilities 24 | 25 | Une page html pour vous permettre d’aller plus vite dans la découverte : https://wfspoc.brgm-rec.fr/geoserver/www/testing_links.html 26 | 27 | - Pour info, exemple hors BRGM (données qualité air) : http://luft.umweltbundesamt.at/inspire/wfs?service=WFS&version=2.0.0&request=GetFeature&typeName=aqd:AQD_SamplingPoint&srsName=EPSG:4258&count=1 28 | 29 | Il s’agit de l’extension du thème Inspire EF pour le rapportage qualité air (tout ce qui est du namespace aqd peut être supprimé) 30 | 31 | 32 | 33 | # INSPIRE thème – Geology– package ‘Hydrogeology’ (GE) 34 | 35 | - http://inspire.ec.europa.eu/schemas/ge_hg/4.0/HydrogeologyCore.xsd 36 | 37 | - Instance : le flux de test devrait être mis en place début 2016 (travail de mapping vers nos bases en cours). 38 | 39 | 40 | 41 | # GroundWaterML2 42 | 43 | - Xsd : http://external.opengeospatial.org/twiki_public/HydrologyDWG/GWML2-XML-Schema 44 | 45 | Prendre la version 2.1 : « Main (units / aquifers, fluid bodies, voids) » et « Well (water wells, monitoring sites, springs) » 46 | 47 | - Instances : 48 | 49 | Flux : https://wfspoc.brgm-rec.fr/constellation/WS/wfs/BRGM:GWML2?service=WFS&version=2.0.0&request=GetCapabilities 50 | 51 | Je vous ai mis en pj un exemple de requêtes post sur le service 52 | 53 | Exemple fichier: https://xp-dev.com/svn/gwml2/Documents/instance/ (GW_Well_BRGM-uc1.xml est un exemple intéressant) 54 | 55 | 56 | 57 | # Eléments complémentaires : 58 | 59 | En l’absence de certains flux, et comme vu en réunion, d’autres exemples de cas complexes. 60 | 61 | ## GeoSciML V4 62 | 63 | - xsd : http://schemas.geosciml.org/geosciml/4.0/ 64 | 65 | - instances : http://schemas.geosciml.org/geosciml/4.0/examples/ 66 | 67 | En première approche : 68 | 69 | - Les flux ‘Inspire’ sont basés surgeoSciMLBasic.xsd 70 | 71 | - Ceux concernant GeologicUnit, MappedFeature et Borehole sont un bon début 72 | 73 | 74 | 75 | ## Inspire thème Ressources Minérales / Minerals4EU 76 | 77 | Minerals4EU (FP7) étend le thème Inspire 78 | 79 | - Xsd : http://minerals4eu.brgm-rec.fr/deegree/resources/appschemas/Minerals4EUExtension.xsd 80 | 81 | - Instance : http://minerals4eu.brgm-rec.fr/deegree/services/m4eu?request=GetFeature&version=2.0.0&service=WFS&typeName=mr-m4eu:MineralOccurrence_Extension&srsName=EPSG:4258&count=1 82 | 83 | 84 | 85 | @Olivier : ces contenus pourront être ajoutés au CR. 86 | 87 | 88 | 89 | Si l’on ne se recroise pas d’ici là, je vous souhaite de bonne fêtes de fin d’année. 90 | 91 | 92 | 93 | Sylvain 94 | -------------------------------------------------------------------------------- /tests/samples/swe/AQD/readMe_endpoints.txt: -------------------------------------------------------------------------------- 1 | # UBA sample query 2 | http://luft.umweltbundesamt.at/inspire/sos?service=SOS&version=2.0.0&request=GetObservation&offering=urn:STA/SPO.09.LAA.1051.8.1&eventTime=2000-12-31T01:00:00/2005-05-27T01:00:00 3 | 4 | # Envirocloud sample query 5 | http://www.envirocloud.se/52n-sos-webapp/service?service=SOS&version=2.0.0&request=GetObservation&featureOfInterest=SPO_F-SE0012R_00007_100_100&temporalFilter=om:phenomenonTime,2016-11-24T00:00:00%2B00:00/2016-11-26T00:00:00%2B00:00&responseFormat=http://dd.eionet.europa.eu/schemaset/id2011850eu-1.0 6 | -------------------------------------------------------------------------------- /tests/tests_qgis.dockerfile: -------------------------------------------------------------------------------- 1 | FROM qgis/qgis:release-3_16 2 | 3 | ENV PYTHONDONTWRITEBYTECODE=1 4 | 5 | RUN mkdir -p /tmp/plugin 6 | WORKDIR /tmp/plugin 7 | 8 | COPY requirements requirements 9 | 10 | RUN python3 -m pip install --no-cache-dir -U pip \ 11 | && python3 -m pip install --no-cache-dir -U setuptools wheel \ 12 | && python3 -m pip install --no-cache-dir -U -r requirements/testing.txt 13 | 14 | RUN qgis --version 15 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BRGM/gml_application_schema_toolbox/fdf214ab5ef9053839f2a8bbf598ae16b33cc28e/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/test_plg_metadata.py: -------------------------------------------------------------------------------- 1 | #! python3 # noqa E265 2 | 3 | """ 4 | Usage from the repo root folder: 5 | 6 | .. code-block:: bash 7 | # for whole tests 8 | python -m unittest tests.test_plg_metadata 9 | # for specific test 10 | python -m unittest tests.test_plg_metadata.TestPluginMetadata.test_version_semver 11 | """ 12 | 13 | # standard library 14 | import unittest 15 | from pathlib import Path 16 | 17 | # 3rd party 18 | from semver import VersionInfo 19 | 20 | # project 21 | from gml_application_schema_toolbox import __about__ 22 | 23 | # ############################################################################ 24 | # ########## Classes ############# 25 | # ################################ 26 | 27 | 28 | class TestPluginMetadata(unittest.TestCase): 29 | def test_metadata_types(self): 30 | """Test types.""" 31 | # plugin metadata.txt file 32 | self.assertIsInstance(__about__.PLG_METADATA_FILE, Path) 33 | self.assertTrue(__about__.PLG_METADATA_FILE.is_file()) 34 | 35 | # plugin dir 36 | self.assertIsInstance(__about__.DIR_PLUGIN_ROOT, Path) 37 | self.assertTrue(__about__.DIR_PLUGIN_ROOT.is_dir()) 38 | 39 | # metadata as dict 40 | self.assertIsInstance(__about__.__plugin_md__, dict) 41 | 42 | # general 43 | self.assertIsInstance(__about__.__author__, str) 44 | self.assertIsInstance(__about__.__copyright__, str) 45 | self.assertIsInstance(__about__.__email__, str) 46 | self.assertIsInstance(__about__.__keywords__, list) 47 | self.assertIsInstance(__about__.__license__, str) 48 | self.assertIsInstance(__about__.__summary__, str) 49 | self.assertIsInstance(__about__.__title__, str) 50 | self.assertIsInstance(__about__.__title_clean__, str) 51 | self.assertIsInstance(__about__.__version__, str) 52 | self.assertIsInstance(__about__.__version_info__, tuple) 53 | 54 | # misc 55 | self.assertLessEqual(len(__about__.__title_clean__), len(__about__.__title__)) 56 | 57 | # QGIS versions 58 | self.assertIsInstance( 59 | __about__.__plugin_md__.get("general").get("qgisminimumversion"), str 60 | ) 61 | 62 | self.assertIsInstance( 63 | __about__.__plugin_md__.get("general").get("qgismaximumversion"), str 64 | ) 65 | 66 | self.assertLessEqual( 67 | float(__about__.__plugin_md__.get("general").get("qgisminimumversion")), 68 | float(__about__.__plugin_md__.get("general").get("qgismaximumversion")), 69 | ) 70 | 71 | def test_version_semver(self): 72 | """Test if version comply with semantic versioning.""" 73 | self.assertTrue(VersionInfo.isvalid(__about__.__version__)) 74 | 75 | 76 | # ############################################################################ 77 | # ####### Stand-alone run ######## 78 | # ################################ 79 | if __name__ == "__main__": 80 | unittest.main() 81 | --------------------------------------------------------------------------------