[Git][debian-gis-team/totalopenstation][master] 8 commits: Merge branch 'upstream' of salsa.debian.org:debian-gis-team/totalopenstation into upstream
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Fri Jan 2 15:24:48 GMT 2026
Bas Couwenberg pushed to branch master at Debian GIS Project / totalopenstation
Commits:
6b239e27 by Matteo F. Vescovi at 2025-03-25T21:29:41+01:00
Merge branch 'upstream' of salsa.debian.org:debian-gis-team/totalopenstation into upstream
- - - - -
3b8fcb2e by Bas Couwenberg at 2026-01-02T15:57:15+01:00
New upstream version 0.7.2
- - - - -
51a213d6 by Bas Couwenberg at 2026-01-02T15:57:16+01:00
Update upstream source from tag 'upstream/0.7.2'
Update to upstream version '0.7.2'
with Debian dir 332e543881670ee8f2d0d28045bba2707094cb58
- - - - -
c4f80d5b by Bas Couwenberg at 2026-01-02T15:57:28+01:00
New upstream release.
- - - - -
b30b9cff by Bas Couwenberg at 2026-01-02T16:06:51+01:00
Update copyright file.
- - - - -
8273a31d by Bas Couwenberg at 2026-01-02T16:08:40+01:00
Annotate build dependencies with nocheck.
- - - - -
b7a7ba86 by Bas Couwenberg at 2026-01-02T16:10:17+01:00
Add pybuild-plugin-pyproject to build dependencies.
- - - - -
5c29b562 by Bas Couwenberg at 2026-01-02T16:23:11+01:00
Fix pytest invocation.
- - - - -
25 changed files:
- .github/workflows/pyinstaller.yml
- .github/workflows/pytest.yml
- debian/changelog
- debian/control
- debian/copyright
- debian/rules
- docs/conf.py
- docs/contributing/main.rst
- docs/output_formats/of_implemented.rst
- docs/requirements.txt
- + pyproject.toml
- + sample_data/leica_gsi/RILIEVO.gsi
- − setup.py
- totalopenstation-gui.spec
- totalopenstation/__init__.py
- totalopenstation/formats/leica_gsi.py
- totalopenstation/output/__init__.py
- totalopenstation/output/tops_csv.py
- + totalopenstation/scripts/__init__.py
- scripts/totalopenstation-cli-connector.py → totalopenstation/scripts/cli_connector.py
- scripts/totalopenstation-cli-parser.py → totalopenstation/scripts/cli_parser.py
- scripts/totalopenstation-gui.py → totalopenstation/scripts/gui.py
- totalopenstation/tests/test_csv.py
- totalopenstation/tests/test_leica_gsi.py
- tox.ini
Changes:
=====================================
.github/workflows/pyinstaller.yml
=====================================
@@ -13,10 +13,10 @@ jobs:
steps:
- uses: actions/checkout at v2
- - name: Set up Python 3.8
+ - name: Set up Python 3.9
uses: actions/setup-python at v2
with:
- python-version: 3.8
+ python-version: 3.9
- name: Install dependencies and package
run: |
python -m pip install --upgrade pip
=====================================
.github/workflows/pytest.yml
=====================================
@@ -5,36 +5,35 @@ name: pytest
on:
push:
- branches: [ main ]
+ branches: [main]
pull_request:
- branches: [ main ]
+ branches: [main]
jobs:
build:
-
runs-on: ${{ matrix.os }}
strategy:
matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11", "pypy3.8"]
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.10"]
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- - uses: actions/checkout at v3
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python at v4
- with:
- python-version: ${{ matrix.python-version }}
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install flake8 pytest
- pip install .
- - name: Lint with flake8
- run: |
- # stop the build if there are Python syntax errors or undefined names
- flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
- # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- - name: Test with pytest
- run: |
- pytest
+ - uses: actions/checkout at v3
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python at v4
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install flake8 pytest
+ pip install .
+ - name: Lint with flake8
+ run: |
+ # stop the build if there are Python syntax errors or undefined names
+ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+ # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+ flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+ - name: Test with pytest
+ run: |
+ pytest
=====================================
debian/changelog
=====================================
@@ -1,12 +1,16 @@
-totalopenstation (0.5.3-2) UNRELEASED; urgency=medium
+totalopenstation (0.7.2-1) UNRELEASED; urgency=medium
* Team upload.
+ * New upstream release.
* Update lintian overrides.
* Drop Rules-Requires-Root: no, default since dpkg 1.22.13.
* Use test-build-validate-cleanup instead of test-build-twice.
* Make pytest output verbose.
+ * Update copyright file.
+ * Annotate build dependencies with nocheck.
+ * Add pybuild-plugin-pyproject to build dependencies.
- -- Bas Couwenberg <sebastic at debian.org> Fri, 12 Sep 2025 17:51:30 +0200
+ -- Bas Couwenberg <sebastic at debian.org> Fri, 02 Jan 2026 15:57:21 +0100
totalopenstation (0.5.3-1) unstable; urgency=medium
=====================================
debian/control
=====================================
@@ -6,9 +6,10 @@ Priority: optional
Build-Depends: debhelper-compat (= 13),
dh-python,
dh-sequence-python3,
+ pybuild-plugin-pyproject,
python3,
python3-pygeoif,
- python3-pytest,
+ python3-pytest <!nocheck>,
python3-serial,
python3-setuptools,
python3-tk
=====================================
debian/copyright
=====================================
@@ -2,14 +2,22 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Total Open Station
Upstream-Contact: Stefano Costa <steko at iosa.it>
Source: https://github.com/totalopenstation/totalopenstation
-Comment: The upstream tarball is repacked to exclude generated files.
Files: *
-Copyright: 2008-2011 Stefano Costa <steko at iosa.it>
+Copyright: 2008-2025, Stefano Costa <steko at iosa.it>
+ 2023, Sebastian Gutwein <bas at rdgland.com>
+ 2021, Enzo Cocca <enzo.ccc at gmail.com>
+ 2015-2019, Damien Gaignon <damien.gaignon at gmail.com>
+ 2008-2012, Luca Bianconi <luxetluc at yahoo.it>
+ 2010, Cristiano Moscaritolo <cristianomoscaritolo at yahoo.it>
+ 2010, Enza Battiante <enza.battiante at alice.it>
+ 2010, Olga Pastore <olga.pastore at gmail.com>
+ 2010, Raffaele Fanelli <rfl.fanelli at libero.it>
+ 2009, Alessandro Bezzi <alessandro.bezzi at arc-team.com>
License: GPL-3+
Files: debian/*
-Copyright: 2012-2025 Matteo F. Vescovi <mfv at debian.org>
+Copyright: 2012-2025, Matteo F. Vescovi <mfv at debian.org>
License: GPL-3+
License: GPL-3+
=====================================
debian/rules
=====================================
@@ -6,6 +6,8 @@ export PYBUILD_BEFORE_TEST=cp -r {dir}/sample_data {build_dir}
export PYBUILD_AFTER_TEST=rm -rf {build_dir}/sample_data
ifeq ($(PYBUILD_AUTOPKGTEST),1)
export PYBUILD_TEST_ARGS=-v --pyargs totalopenstation
+else
+export PYBUILD_TEST_ARGS=-v $(CURDIR)/totalopenstation/tests
endif
INSTDIR=debian/tmp
=====================================
docs/conf.py
=====================================
@@ -60,9 +60,9 @@ author = 'Stefano Costa'
# built documents.
#
# The short X.Y version.
-version = '0.5'
+version = '0.7'
# The full version, including alpha/beta/rc tags.
-release = '0.5.3'
+release = '0.7.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
=====================================
docs/contributing/main.rst
=====================================
@@ -107,21 +107,6 @@ independently, with the exception of the user interfaces.
In other words, the classes for reading specific formats and those for writing
well-known formats are entirely usable on their own.
-This is a feature.
-
-Example: a web app for converting total station data
-====================================================
-
-If you want to see how to write a web app to convert total station
-data in 50 lines of Python code, check out `TOPS in the Cloud
-<https://bitbucket.org/steko/tops-cloud/overview>`_. It is made with
-`Flask <http://flask.pocoo.org/>`_ and shows how to use Total Open
-Station as a programming library.
-
-.. warning::
- TOPS in the Cloud is not maintained and does not receive security
- updates. Please don't use it in production.
-
==================================
Developing with Total Open Station
==================================
@@ -304,8 +289,8 @@ For more in-depth knowledge of classes, we encourage reading the code @ `Github`
Documentation
=============
-The documentation is included in the source tree, and is published
-online at `http://totalopenstation.readthedocs.org/ <http://totalopenstation.readthedocs.org/>`_.
+The documentation is included in the ``docs`` directory of the source tree, and is published
+online at `http://totalopenstation.readthedocs.io/ <http://totalopenstation.readthedocs.io/>`_.
Manual pages for the three scripts provided with TOPS are not
available at the moment.
@@ -321,12 +306,12 @@ A *source distribution* is made using::
python setup.py sdist
-A *built distribution* is made using (e.g. for Windows installer)::
+A *built distribution* is made using the *wheel* format (this requires the ``wheel`` package)::
- python setup.py bdist --formats wininst
+ python setup.py bdist_wheel
We are currently following the `Python Packaging User Guide
-<https://packaging.python.org/en/latest/distributing.html>`_ and
+<https://packaging.python.org/>`_ and
distributing sources and *wheels*.
Windows portable app
=====================================
docs/output_formats/of_implemented.rst
=====================================
@@ -58,6 +58,13 @@ Yet, this format is not parametric and values return are the following::
PID, type, Point Name, x, y, angle, z_angle, distance, th, ih, circle, station
+Trimble CSV
+-----------
+
+.. versionadded:: 0.7
+
+This module contains also a variant CSV output with the same data columns,
+but in a different order, Trimble CSV (used for LandSurveyCodesImport QGIS plugin)::
======================
:mod:`tops_dat` -- DAT
=====================================
docs/requirements.txt
=====================================
@@ -1,4 +1 @@
Sphinx==8.1.3
-pygeoif==0.7
-pytest==6.2.5
-pyserial==3.5
=====================================
pyproject.toml
=====================================
@@ -0,0 +1,55 @@
+[build-system]
+requires = ["setuptools >= 77.0.3"]
+build-backend = "setuptools.build_meta"
+
+[project]
+dynamic = ["version", "readme"]
+name = "totalopenstation"
+dependencies = ["pyserial==3.5", "pygeoif==1.4.0"]
+requires-python = ">= 3.9"
+authors = [
+ { name = "Stefano Costa" },
+ { name = "Damien Gaignon" },
+ { name = "Luca Bianconi" },
+]
+description = "Download and export survey data from your total station"
+license = "GPL-3.0"
+keywords = ["survey", "geodimeter", "fieldwork", "format conversion"]
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Environment :: Console",
+ "Environment :: X11 Applications",
+ "Intended Audience :: End Users/Desktop",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Topic :: Scientific/Engineering :: GIS",
+]
+
+[project.urls]
+Homepage = "https://tops.iosa.it/"
+Documentation = "https://totalopenstation.readthedocs.io/"
+Repository = "https://github.com/totalopenstation/totalopenstation.git"
+Issues = "https://github.com/totalopenstation/totalopenstation/issues"
+
+[project.scripts]
+totalopenstation-cli-parser = "totalopenstation.scripts.cli_parser:cli_parser"
+totalopenstation-cli-connector = "totalopenstation.scripts.cli_connector:cli_connector"
+
+[project.gui-scripts]
+totalopenstation-gui = "totalopenstation.scripts.gui:gui"
+
+[tool.setuptools.dynamic]
+version = { attr = "totalopenstation.__version__" }
+readme = { file = ["README.rst"] }
+
+[tool.setuptools]
+packages = ["totalopenstation"]
+
+[dependency-groups]
+test = ["pytest>=5.1"]
=====================================
sample_data/leica_gsi/RILIEVO.gsi
=====================================
@@ -0,0 +1 @@
+
110001+00000100 21.102+11545200 22.102+09885300 31..00+00000000 32..00+00000000
110002+00000101 21.102+00000000 22.102+09681000 31..00+00000000 32..00+00000000
110003+00000102 21.102+13247100 22.102+10378200 31..00+00005165 32..00+00005156
110004+00000103 21.102+13603500 22.102+10381400 31..00+00005351 32..00+00005341
110005+00000104 21.102+14438100 22.102+10620800 31..00+00004636 32..00+00004614
110006+00000105 21.102+14579100 22.102+10685200 31..00+00004232 32..00+00004208
110007+00000106 21.102+15288600 22.102+11935000 31..00+00003966 32..00+00003784
110008+00000107 21.102+02838500 22.102+10620000 31..00+00003866 32..00+00003848
110009+00000108 21.102+05895200 22.102+10233100 31..00+00006454 32..00+00006450
110010+00000109 21.102+07337500 22.102+10190100 31..00+00011374 32..00+00011369
110011+00000110 21.102+08174000 22.102+10127600 31..00+00020332 32..00+00020328
110012+00000111 21.102+09088000 22.102+09955300 31..00+00026759 32..00+00026758
110013+00000112 21.102+14020300 22.102+09968000 31..00+00036927 32..00+00036927
110014+00000113 21.102+18508800 22.102+10066300 31..00+00033477 32..00+00033475
110015+00000114 21.102+18689100 22.102+09962400 31..00+00050672 32..00+00050671
110016+00000115 21.102+18446500 22.102+10038200 31..00+00065824 32..00+00065823
110017+00000116 21.102+16740300 22.102+10037300 31..00+00072037 32..00+00072036
110018+00000117 21.102+16672100 22.102+09992200 31..00+00069647 32..00+00069647
110019+00000118 21.102+23066200 22.102+10080900 31..00+00041345 32..00+00041342
110020+00000119 21.102+28207100 22.102+10125200 31..00+00024342 32..00+00024337
110021+00000120 21.102+28333600 22.102+10062400 31..00+00067652 32..00+00067649
110022+00000121 21.102+25011800 22.102+10043700 31..00+00083285 32..00+00083283
110023+00000122 21.102+04392100 22.102+10354100 31..00+00004600 32..00+00004593
\ No newline at end of file
=====================================
setup.py deleted
=====================================
@@ -1,37 +0,0 @@
-from setuptools import setup, find_packages
-
-import totalopenstation
-
-setup(
- name='totalopenstation',
- version=totalopenstation.__version__,
- author='Stefano Costa',
- author_email='steko at iosa.it',
- packages=find_packages(exclude=['ez_setup', 'examples', 'tests', 'gui']),
- scripts=['scripts/totalopenstation-gui.py',
- 'scripts/totalopenstation-cli-parser.py',
- 'scripts/totalopenstation-cli-connector.py'],
- url='https://tops.iosa.it/',
- license='GNU GPLv3',
- description='Download and export survey data from your total station',
- long_description=open('README.rst').read(),
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'Environment :: Console',
- 'Environment :: X11 Applications',
- 'Intended Audience :: End Users/Desktop',
- 'License :: OSI Approved :: GNU General Public License (GPL)',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3 :: Only',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.8',
- 'Topic :: Scientific/Engineering :: GIS',
- ],
- keywords='survey geodimeter',
- install_requires=['pyserial==3.4', 'pygeoif==1.4.0'],
- tests_require=['pytest>=5.1'],
- include_package_data = True,
- zip_safe = False,
-)
=====================================
totalopenstation-gui.spec
=====================================
@@ -1,5 +1,5 @@
# -*- mode: python -*-
-a = Analysis(['scripts/totalopenstation-gui.py'],
+a = Analysis(['./totalopenstation/scripts/gui.py'],
pathex=['.'],
hiddenimports=[
'csv',
@@ -7,6 +7,7 @@ a = Analysis(['scripts/totalopenstation-gui.py'],
'numbers',
'totalopenstation.formats',
'totalopenstation.formats.carlson_rw5',
+ 'totalopenstation.formats.landxml',
'totalopenstation.formats.leica_gsi',
'totalopenstation.formats.leica_tcr_705',
'totalopenstation.formats.leica_tcr_1205',
@@ -31,6 +32,7 @@ a = Analysis(['scripts/totalopenstation-gui.py'],
'totalopenstation.output.tops_dat',
'totalopenstation.output.tops_dxf',
'totalopenstation.output.tops_geojson',
+ 'totalopenstation.output.tops_landxml',
'totalopenstation.output.tops_sql',
'totalopenstation.output.tops_txt',],
hookspath=None,
=====================================
totalopenstation/__init__.py
=====================================
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-__version__ = '0.5.3'
+__version__ = '0.7.2'
import logging
=====================================
totalopenstation/formats/leica_gsi.py
=====================================
@@ -208,7 +208,15 @@ class FormatParser(Parser):
'data': t[7:],
}
self.tdict[data['wordindex']] = data
-
+
+ if list(self.tdict.keys()) == (['11', '21', '22', '31', '32']):
+ self.tdict['84'] = {'wordindex': '84', 'info': '...0', 'sign': '+', 'data': '00000000'}
+ self.tdict['85'] = {'wordindex': '85', 'info': '...0', 'sign': '+', 'data': '00000000'}
+ self.tdict['86'] = {'wordindex': '86', 'info': '...0', 'sign': '+', 'data': '00000000'}
+ self.tdict['87'] = {'wordindex': '87', 'info': '...0', 'sign': '+', 'data': '00000000'}
+ self.tdict['88'] = {'wordindex': '88', 'info': '...0', 'sign': '+', 'data': '00000000'}
+ logger.info(f"Old GSI version detected, some values have been set to 0 by default")
+ logger.info(f"see https://github.com/totalopenstation/totalopenstation/issues/168")
try:
pid = int(self.tdict['11']['info'])
text = self.tdict['11']['data'].lstrip('0')
=====================================
totalopenstation/output/__init__.py
=====================================
@@ -7,19 +7,19 @@ class Builder:
def __init__(self, data):
"""Init method which **must** be overridden in the child class
to have a working builder.
-
+
Args:
data (:class:`formats.Parser`): A list of :class:`formats.Feature`
"""
self.data = data
-
+
def process(self):
"""Action for building the output string.
-
+
This method **must** be overridden in the child class
to have a working builder.
-
+
Process the input data (processing data).
This is because we want to keep the generation of output separated from
saving it to disk.
@@ -38,5 +38,6 @@ BUILTIN_OUTPUT_FORMATS = {
'dat': ('tops_dat', 'OutputFormat', 'DAT'),
'txt': ('tops_txt', 'OutputFormat', 'Text'),
'geojson': ('tops_geojson', 'OutputFormat', 'GeoJSON'),
- 'landxml': ('tops_landxml', 'OutputFormat', 'LandXML')
+ 'landxml': ('tops_landxml', 'OutputFormat', 'LandXML'),
+ 'trimblecsv': ('tops_csv', 'TrimbleOutputFormat', 'Trimble CSV')
}
=====================================
totalopenstation/output/tops_csv.py
=====================================
@@ -75,3 +75,72 @@ class OutputFormat(Builder):
self.writer.writerow(row)
return self.output.getvalue()
+
+class TrimbleOutputFormat(OutputFormat):
+ """
+ Exports points data in Trimble CSV format,
+ used for LandSurveyCodesImport QGIS plugin.
+
+ ``data`` should be an iterable containing Feature objects.
+ """
+
+ def __init__(self, data):
+ self.data = data
+ self.output = io.StringIO()
+ fieldnames = [
+ "id",
+ "x",
+ "y",
+ "z",
+ "type",
+ "point_name",
+ "angle",
+ "z_angle",
+ "distance",
+ "th",
+ "ih",
+ "circle",
+ "station",
+ ]
+ self.writer = csv.DictWriter(
+ self.output, quoting=csv.QUOTE_NONNUMERIC, fieldnames=fieldnames
+ )
+ self.writer.writeheader()
+
+ def process(self):
+
+ for feature in self.data:
+ row = {
+ "id": feature.id,
+ "type": feature.desc,
+ "x": feature.geometry.x,
+ "y": feature.geometry.y,
+ }
+
+ try: # not all input formats include z coordinates
+ row["z"] = feature.geometry.z
+ except ValueError:
+ row["z"] = ""
+
+ # a few cases with simple yes/no logic
+ for prop in ["point_name", "ih", "circle", "z_angle", "th"]:
+ row[prop] = feature.properties.get(
+ prop, ""
+ ) # empty string as default value
+
+ # not all input formats include azimuth/angle
+ row["angle"] = feature.properties.get(
+ "azimuth", feature.properties.get("angle", "")
+ )
+
+ # not all input formats include distance
+ row["distance"] = feature.properties.get(
+ "slope_dist", feature.properties.get("horizontal_dist", "")
+ )
+
+ # not all input formats include station name
+ row["station"] = feature.properties.get("st_name", "")
+
+ self.writer.writerow(row)
+
+ return self.output.getvalue()
=====================================
totalopenstation/scripts/__init__.py
=====================================
=====================================
scripts/totalopenstation-cli-connector.py → totalopenstation/scripts/cli_connector.py
=====================================
@@ -1,7 +1,7 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# filename: totalopenstation-cli-connector.py
-# Copyright 2021 Stefano Costa <steko at iosa.it>
+# Copyright 2025 Stefano Costa <steko at iosa.it>
# This file is part of Total Open Station.
@@ -34,71 +34,76 @@ from totalopenstation.models import BUILTIN_MODELS
t = gettext.translation('totalopenstation', './locale', fallback=True)
_ = t.gettext
-usage = _("Usage: %prog [option] arg1 [option] arg2 ...")
-
-parser = OptionParser(usage=usage)
-parser.add_option("-m",
- "--model",
- action="store",
- type="string",
- dest="model",
- help=_("Select input MODEL"),
- metavar="MODEL")
-parser.add_option("-p",
- "--port",
- action="store",
- type="string",
- dest="port",
- help=_("Select input SERIAL PORT"),
- metavar="PORT")
-parser.add_option("-o",
- "--outfile",
- action="store",
- type="string",
- dest="outfile",
- help=_("Select output FILE (do not specify for stdout)"),
- metavar="FILE")
-
-(options, args) = parser.parse_args()
-
-if not (options.model and options.port):
- sys.exit(_("Please specify your model and the port to download from"))
-
-modelclass = BUILTIN_MODELS[options.model]
-
-# import input format parser
-if isinstance(modelclass, tuple):
+def cli_connector():
+ usage = _("Usage: %prog [option] arg1 [option] arg2 ...")
+
+ parser = OptionParser(usage=usage)
+ parser.add_option("-m",
+ "--model",
+ action="store",
+ type="string",
+ dest="model",
+ help=_("Select input MODEL"),
+ metavar="MODEL")
+ parser.add_option("-p",
+ "--port",
+ action="store",
+ type="string",
+ dest="port",
+ help=_("Select input SERIAL PORT"),
+ metavar="PORT")
+ parser.add_option("-o",
+ "--outfile",
+ action="store",
+ type="string",
+ dest="outfile",
+ help=_("Select output FILE (do not specify for stdout)"),
+ metavar="FILE")
+
+ (options, args) = parser.parse_args()
+
+ if not (options.model and options.port):
+ sys.exit(_("Please specify your model and the port to download from"))
+
+ modelclass = BUILTIN_MODELS[options.model]
+
+ # import input format parser
+ if isinstance(modelclass, tuple):
+ try:
+ # builtin format parser
+ mod, cls, name = modelclass
+ modelclass = getattr(
+ __import__('totalopenstation.models.%s' % mod, None, None, [cls]), cls)
+ except ImportError as msg:
+ sys.exit(_('Error loading the required model module: %s') % msg)
+
+ station = modelclass(options.port)
try:
- # builtin format parser
- mod, cls, name = modelclass
- modelclass = getattr(
- __import__('totalopenstation.models.%s' % mod, None, None, [cls]), cls)
- except ImportError as msg:
- sys.exit(_('Error loading the required model module: %s') % msg)
-
-station = modelclass(options.port)
-try:
- station.close() # sometimes the port will be already open for no reason
- station.open()
-except serial.SerialException as detail:
- sys.exit(detail)
-
-print(_("Now you can start download from %s device") % options.model)
-
-station.start()
-station.dl_started.wait()
-print(_("Download started..."))
-station.dl_finished.wait()
-print(_("Download finished..."))
-result = station.result
-
-if options.outfile:
- if not os.path.exists(options.outfile):
- e = open(options.outfile, "wb")
- e.write(result)
- e.close()
- print(_("Downloaded data saved to out file %s") % options.outfile)
+ station.close() # sometimes the port will be already open for no reason
+ station.open()
+ except serial.SerialException as detail:
+ sys.exit(detail)
+
+ print(_("Now you can start download from %s device") % options.model)
+
+ station.start()
+ station.dl_started.wait()
+ print(_("Download started..."))
+ station.dl_finished.wait()
+ print(_("Download finished..."))
+ result = station.result
+
+ if options.outfile:
+ if not os.path.exists(options.outfile):
+ e = open(options.outfile, "wb")
+ e.write(result)
+ e.close()
+ print(_("Downloaded data saved to out file %s") % options.outfile)
+ else:
+ sys.exit(_("Specified output file already exists\n"))
else:
- sys.exit(_("Specified output file already exists\n"))
-else:
- sys.stdout.write(result)
+ sys.stdout.write(result)
+
+
+if __name__ == '__main__':
+ cli_connector()
=====================================
scripts/totalopenstation-cli-parser.py → totalopenstation/scripts/cli_parser.py
=====================================
@@ -36,149 +36,148 @@ import totalopenstation.output
t = gettext.translation('totalopenstation', './locale', fallback=True)
_ = t.gettext
-usage = _("Usage: %prog [option] arg1 [option] arg2 ...")
-
-parser = OptionParser(usage=usage)
-parser.add_option("-i",
- "--infile",
- action="store",
- type="string",
- dest="infile",
- help=_("Select input FILE (do not specify for stdin)"),
- metavar="FILE")
-parser.add_option("-o",
- "--outfile",
- action="store",
- type="string",
- dest="outfile",
- help=_("Select output FILE (do not specify for stdout)"),
- metavar="FILE")
-parser.add_option("-f",
- "--input-format",
- action="store",
- type="string",
- dest="informat",
- help=_("Select input FORMAT"),
- metavar="FORMAT")
-parser.add_option("--2d",
- action="store_true",
- dest="xy_only",
- help=_("Exclude Z coordinates, output only 2D data"),
- metavar="ONLY2D")
-parser.add_option("-t",
- "--output-format",
- action="store",
- type="string",
- dest="outformat",
- help=_("Select input FORMAT"),
- metavar="FORMAT")
-parser.add_option("-r",
- "--raw",
- action="store_true",
- dest="raw",
- help=_("Enhanced parsed file process"))
-parser.add_option(
- "--overwrite",
- action="store_true",
- dest="overwrite",
- default=False,
- help=_("Overwrite existing output file"))
-parser.add_option(
- "--list",
- action="store_true",
- dest="list",
- default=False,
- help=_("List the available input and output formats"))
-parser.add_option(
- "--log",
- action="store",
- dest="loglevel",
- default="WARNING",
- type="string",
- help=_("Minimum log level"))
-parser.add_option(
- "--logtofile",
- action="store_true",
- dest="logotfile",
- default=False,
- help=_("Log to file"))
-
-
-(options, args) = parser.parse_args()
-
-logger = logging.getLogger()
-logger.setLevel(options.loglevel.upper())
-if options.logotfile:
- handler = logging.FileHandler("tops.log")
-else:
- handler = logging.StreamHandler()
-formatter = logging.Formatter('%(asctime)s -- %(filename)s -- %(levelname)s -- %(funcName)s -- %(message)s')
-handler.setFormatter(formatter)
-logger.addHandler(handler)
-
-def list_formats():
- '''Print a list of the supported input and output formats.'''
-
- mod_string = _("List of supported input formats:\n%s\n") % ('-' * 30)
- for k, v in sorted(totalopenstation.formats.BUILTIN_INPUT_FORMATS.items()):
- mod_string += "%s%s\n" % (k.ljust(20), v[2])
- mod_string += "\n\n"
-
- mod_string += _("List of supported output formats:\n%s\n") % ('-' * 30)
- for k, v in sorted(totalopenstation.output.BUILTIN_OUTPUT_FORMATS.items()):
- mod_string += "%s%s\n" % (k.ljust(20), v[2])
- mod_string += "\n"
- return mod_string
-
-if options.list:
- sys.stdout.write(list_formats())
- sys.exit()
-
-def exit_with_error(message):
- sys.exit(_("\nError:\n%(message)s\n\n%(formats)s") % {'message': message,
- 'formats': list_formats()})
-
-if options.informat:
- try:
- inputclass = totalopenstation.formats.BUILTIN_INPUT_FORMATS[options.informat]
- except KeyError as message:
- exit_with_error(_('%s is not a valid input format') % message)
+def cli_parser():
+ usage = _("Usage: %prog [option] arg1 [option] arg2 ...")
+
+ parser = OptionParser(usage=usage)
+ parser.add_option("-i",
+ "--infile",
+ action="store",
+ type="string",
+ dest="infile",
+ help=_("Select input FILE (do not specify for stdin)"),
+ metavar="FILE")
+ parser.add_option("-o",
+ "--outfile",
+ action="store",
+ type="string",
+ dest="outfile",
+ help=_("Select output FILE (do not specify for stdout)"),
+ metavar="FILE")
+ parser.add_option("-f",
+ "--input-format",
+ action="store",
+ type="string",
+ dest="informat",
+ help=_("Select input FORMAT"),
+ metavar="FORMAT")
+ parser.add_option("--2d",
+ action="store_true",
+ dest="xy_only",
+ help=_("Exclude Z coordinates, output only 2D data"),
+ metavar="ONLY2D")
+ parser.add_option("-t",
+ "--output-format",
+ action="store",
+ type="string",
+ dest="outformat",
+ help=_("Select input FORMAT"),
+ metavar="FORMAT")
+ parser.add_option("-r",
+ "--raw",
+ action="store_true",
+ dest="raw",
+ help=_("Enhanced parsed file process"))
+ parser.add_option(
+ "--overwrite",
+ action="store_true",
+ dest="overwrite",
+ default=False,
+ help=_("Overwrite existing output file"))
+ parser.add_option(
+ "--list",
+ action="store_true",
+ dest="list",
+ default=False,
+ help=_("List the available input and output formats"))
+ parser.add_option(
+ "--log",
+ action="store",
+ dest="loglevel",
+ default="WARNING",
+ type="string",
+ help=_("Minimum log level"))
+ parser.add_option(
+ "--logtofile",
+ action="store_true",
+ dest="logotfile",
+ default=False,
+ help=_("Log to file"))
+
+
+ (options, args) = parser.parse_args()
+
+ logger = logging.getLogger()
+ logger.setLevel(options.loglevel.upper())
+ if options.logotfile:
+ handler = logging.FileHandler("tops.log")
else:
- if isinstance(inputclass, tuple):
- try:
- # builtin format parser
- mod, cls, name = inputclass
- inputclass = getattr(importlib.import_module('totalopenstation.formats.%s' % mod), cls)
- except ImportError as message:
- exit_with_error(message)
-else:
- sys.exit(_("Please specify an input format"))
-
-if options.outformat:
- try:
- outputclass = totalopenstation.output.BUILTIN_OUTPUT_FORMATS[options.outformat]
- except KeyError as message:
- exit_with_error(_('%s is not a valid output format') % message)
- else:
- if isinstance(outputclass, tuple):
- try:
- # builtin output builder
- mod, cls, name = outputclass
- outputclass = getattr(importlib.import_module('totalopenstation.output.%s' % mod), cls)
- except ImportError as message:
- exit_with_error(message)
-
-if options.infile:
- infile = open(options.infile, 'r').read()
-else:
- if sys.stdin.isatty():
- sys.exit(_('No input data!'))
+ handler = logging.StreamHandler()
+ formatter = logging.Formatter('%(asctime)s -- %(filename)s -- %(levelname)s -- %(funcName)s -- %(message)s')
+ handler.setFormatter(formatter)
+ logger.addHandler(handler)
+
+ def list_formats():
+ '''Print a list of the supported input and output formats.'''
+
+ mod_string = _("List of supported input formats:\n%s\n") % ('-' * 30)
+ for k, v in sorted(totalopenstation.formats.BUILTIN_INPUT_FORMATS.items()):
+ mod_string += "%s%s\n" % (k.ljust(20), v[2])
+ mod_string += "\n\n"
+
+ mod_string += _("List of supported output formats:\n%s\n") % ('-' * 30)
+ for k, v in sorted(totalopenstation.output.BUILTIN_OUTPUT_FORMATS.items()):
+ mod_string += "%s%s\n" % (k.ljust(20), v[2])
+ mod_string += "\n"
+ return mod_string
+
+ if options.list:
+ sys.stdout.write(list_formats())
+ sys.exit()
+
+ def exit_with_error(message):
+ sys.exit(_("\nError:\n%(message)s\n\n%(formats)s") % {'message': message,
+ 'formats': list_formats()})
+
+ if options.informat:
+ try:
+ inputclass = totalopenstation.formats.BUILTIN_INPUT_FORMATS[options.informat]
+ except KeyError as message:
+ exit_with_error(_('%s is not a valid input format') % message)
+ else:
+ if isinstance(inputclass, tuple):
+ try:
+ # builtin format parser
+ mod, cls, name = inputclass
+ inputclass = getattr(importlib.import_module('totalopenstation.formats.%s' % mod), cls)
+ except ImportError as message:
+ exit_with_error(message)
else:
- infile = sys.stdin.read()
+ sys.exit(_("Please specify an input format"))
+ if options.outformat:
+ try:
+ outputclass = totalopenstation.output.BUILTIN_OUTPUT_FORMATS[options.outformat]
+ except KeyError as message:
+ exit_with_error(_('%s is not a valid output format') % message)
+ else:
+ if isinstance(outputclass, tuple):
+ try:
+ # builtin output builder
+ mod, cls, name = outputclass
+ outputclass = getattr(importlib.import_module('totalopenstation.output.%s' % mod), cls)
+ except ImportError as message:
+ exit_with_error(message)
+
+ if options.infile:
+ infile = open(options.infile, 'r').read()
+ else:
+ if sys.stdin.isatty():
+ sys.exit(_('No input data!'))
+ else:
+ infile = sys.stdin.read()
-def main(infile):
- '''After setting up all parameters, finally try to process input data.'''
+ # After setting up all parameters, finally try to process input data.
parsed_data = inputclass(infile)
if options.raw:
@@ -216,4 +215,4 @@ def main(infile):
sys.stdout.write(output.process())
if __name__ == '__main__':
- main(infile)
+ cli_parser()
=====================================
scripts/totalopenstation-gui.py → totalopenstation/scripts/gui.py
=====================================
@@ -1,7 +1,7 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# filename: totalopenstation-gui.py
-# Copyright 2008-2019 Stefano Costa <steko at iosa.it>
+# Copyright 2008-2025 Stefano Costa <steko at iosa.it>
# Copyright 2010,2012 Luca Bianconi <luxetluc at yahoo.it>
#
# This file is part of Total Open Station.
@@ -119,7 +119,7 @@ class AboutDialog(tkinter.simpledialog.Dialog):
def body(self, master):
title = "Total Open Station %s" % totalopenstation.__version__
message = _("""
-Total Open Station is copyright 2008-2019 Stefano Costa, Damien
+Total Open Station is copyright 2008-2025 Stefano Costa, Damien
Gaignon, Luca Bianconi and the IOSA project, under the GNU GPL v3
or any later version.
@@ -802,14 +802,14 @@ class Tops:
self.text_area.update_idletasks()
-root = Tk()
-Tops = Tops(root)
+def gui():
+ root = Tk()
+ app = Tops(root)
+ #save user's preferences (model, port and sleeptime if custom model)
-#save user's preferences (model, port and sleeptime if custom model)
-
-atexit.register(Tops.upref.setvalues,
- {'model': Tops.optionMODEL_value.get(),
- 'port': Tops.option1_value.get(),
- 'sleeptime': Tops.option6_value.get(),
- })
+ atexit.register(app.upref.setvalues,
+ {'model': app.optionMODEL_value.get(),
+ 'port': app.option1_value.get(),
+ 'sleeptime': app.option6_value.get(),
+ })
=====================================
totalopenstation/tests/test_csv.py
=====================================
@@ -1,7 +1,7 @@
import unittest
from totalopenstation.formats import Feature, Point
-from totalopenstation.output.tops_csv import OutputFormat
+from totalopenstation.output.tops_csv import OutputFormat, TrimbleOutputFormat
class TestCSVOutput(unittest.TestCase):
@@ -20,3 +20,20 @@ class TestCSVOutput(unittest.TestCase):
def test_output(self):
self.output = OutputFormat(self.data).process()
self.assertEqual(self.output.splitlines()[1], '1,"PT","TEST POINT",12.8,76.3,56.2,"","","","","","",""')
+
+class TestTrimbleCSVOutput(unittest.TestCase):
+
+ def setUp(self):
+ self.data = [
+ Feature(Point(12.8, 76.3, 56.2), desc="PT", point_name="TEST POINT", id=1),
+ Feature(
+ Point(19.8, 26.3, 46.2), desc="PT", point_name="TEST POINT #2", id=2
+ ),
+ ]
+
+ def test_output(self):
+ self.output = TrimbleOutputFormat(self.data).process()
+ self.assertEqual(
+ self.output.splitlines()[1],
+ '1,12.8,76.3,56.2,"PT","TEST POINT","","","","","","",""',
+ )
=====================================
totalopenstation/tests/test_leica_gsi.py
=====================================
@@ -70,3 +70,20 @@ class TestLeicaGSI8Output(BaseTestOutput):
def setup(self):
with open('sample_data/leica_gsi/leica_gsi16_gurob.gsi') as testdata:
self.fp = FormatParser(testdata.read())
+
+
+class TestLeicaGSIOldParser(unittest.TestCase):
+ def setUp(self):
+ with open('sample_data/leica_gsi/RILIEVO.gsi') as testdata:
+ self.fp = FormatParser(testdata.read())
+
+ def test_all_points_processed(self):
+ self.assertEqual(len(self.fp.points), 23)
+
+ def test_values(self):
+ self.assertEqual(self.fp.points[2].id, 3)
+ self.assertEqual(self.fp.points[2].desc, 'PT')
+ self.assertEqual(self.fp.points[2].point_name, '102')
+ self.assertAlmostEqual(self.fp.points[2].geometry.x, 4.49963916)
+ self.assertAlmostEqual(self.fp.points[2].geometry.y, -2.51722711)
+ self.assertAlmostEqual(self.fp.points[2].geometry.z, -0.30665937)
=====================================
tox.ini
=====================================
@@ -1,10 +1,10 @@
[tox]
env_list =
- py38
py39
py310
py311
py312
+ py313
minversion = 4.14.2
[testenv]
View it on GitLab: https://salsa.debian.org/debian-gis-team/totalopenstation/-/compare/349ac604ef178ab285f0072ee991ff3f33f99ca7...5c29b562b815cbb250ec6a65112b04848e64ce40
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/totalopenstation/-/compare/349ac604ef178ab285f0072ee991ff3f33f99ca7...5c29b562b815cbb250ec6a65112b04848e64ce40
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260102/bf1b3f3a/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list