diff --git a/docs/about/CSVDump.md b/docs/about/CSVDump.md deleted file mode 100644 index 49f1301ee..000000000 --- a/docs/about/CSVDump.md +++ /dev/null @@ -1,48 +0,0 @@ -## CSVDump.py - -CSVDump.py is a Python script that converts a CSV file downloaded from -ODK Central to OpenStreetMap (OSM) XML format. The tool can be useful -for users who want to work with OpenStreetMap data and want to convert -ODK Central data into a compatible format. - - options: - -h, --help - show this help message and exit - -v, --verbose - verbose output - -i CSVFILE, --infile CSVFILE - Specifies the path and filename of the input CSV file downloaded from ODK Central. This option is required for the program to run. - -### Examples - -To convert a CSV file named "survey_data.csv" located in the current -working directory, the following command can be used: - - [path]/CSVDump.py -i survey_data.csv - -To enable verbose output during the conversion process, the following -command can be used: - - [path]/CSVDump.py -i survey_data.csv -v - -### Input Format - -CSVDump.py expects an input file in CSV format downloaded from ODK -Central. The CSV file should have a header row with column names that -correspond to the survey questions. Each row in the CSV file should -contain a response to the survey questions, with each column -representing a different question. - -### Output Format - -The output of CSVDump.py is an OSM XML file that can be used with -OpenStreetMap data tools and services. The converted OSM XML file will -have tags for each survey question in the CSV file, as well as any -metadata associated with the survey. The format of the OSM XML file -generated by CSVDump.py is compatible with other OpenStreetMap data -tools and services. - -### Limitations - -- CSVDump.py only supports CSV files downloaded from ODK - Central. Other CSV files may not be compatible with the tool. -- The tool only supports simple data types such as strings, numbers, - and dates. Complex data types such as arrays and nested structures - are not supported. diff --git a/docs/about/odk2csv.md b/docs/about/odk2csv.md deleted file mode 100644 index 780905028..000000000 --- a/docs/about/odk2csv.md +++ /dev/null @@ -1,12 +0,0 @@ -# odk2csv - -Convert ODK XML instance file to CSV format - - usage: odk2csv [-h] [-v [VERBOSE]] -i INSTANCE - - options: - -h, --help show this help message and exit - -v [VERBOSE], --verbose [VERBOSE] - verbose output - -i INSTANCE, --instance INSTANCE - The instance file(s) from ODK Collect diff --git a/docs/about/odk2geojson.md b/docs/about/odk2geojson.md deleted file mode 100644 index f3aed30a1..000000000 --- a/docs/about/odk2geojson.md +++ /dev/null @@ -1,14 +0,0 @@ -# odk2geojson - -Convert ODK XML instance file to GeoJson - - usage: odk2geojson [-h] [-v [VERBOSE]] -i INSTANCE [-o OUTFILE] - - options: - -h, --help show this help message and exit - -v [VERBOSE], --verbose [VERBOSE] - verbose output - -i INSTANCE, --instance INSTANCE - The instance file(s) from ODK Collect - -o OUTFILE, --outfile OUTFILE - The output file for JOSM diff --git a/docs/about/osm2favorites.md b/docs/about/osm2favorites.md deleted file mode 100644 index 0c609087b..000000000 --- a/docs/about/osm2favorites.md +++ /dev/null @@ -1,16 +0,0 @@ -# osm2favorities.py - -This is a simple utility that generates a GPX file from OSM data or a -GeoJson file for Osmand. This makes the data a available under **My -Places** in the Osmand menu.This is useful for a field mapping project -that covers a large area, but with a few small areas of interest. This -makes them all readily available for navigation. For some features -this program also adds Osmand styling to change the displayed icons -and colors. - -## options - - -h, --help show this help message and exit - -v, --verbose verbose output - -i INFILE, --infile INFILE - The data extract diff --git a/docs/api/ODKForm.md b/docs/api/ODKForm.md deleted file mode 100644 index c5b761745..000000000 --- a/docs/api/ODKForm.md +++ /dev/null @@ -1,3 +0,0 @@ -# OdkForm - -::: osm_fieldwork.ODKForm diff --git a/docs/api/ODKInstance.md b/docs/api/ODKInstance.md deleted file mode 100644 index 3affbf000..000000000 --- a/docs/api/ODKInstance.md +++ /dev/null @@ -1,3 +0,0 @@ -# OdkForm - -::: osm_fieldwork.ODKInstance diff --git a/osm_fieldwork/ODKDump.py b/osm_fieldwork/ODKDump.py deleted file mode 100755 index 5e54620d0..000000000 --- a/osm_fieldwork/ODKDump.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/python3 - -# -# Copyright (C) 2020, 2021, 2022, 2023 Humanitarian OpenstreetMap Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# - -import argparse -import logging -import re -import sys -from datetime import datetime - -from osm_fieldwork.convert import Convert -from osm_fieldwork.ODKForm import ODKForm -from osm_fieldwork.ODKInstance import ODKInstance -from osm_fieldwork.osmfile import OsmFile - -if __name__ != "__main__": - print("This is not a loadable python module!") - exit - -log = logging.getLogger(__name__) - -parser = argparse.ArgumentParser() -parser.add_argument("-v", "--verbose", action="store_true", help="verbose output") -parser.add_argument("-x", "--xform", required=True, help="input xform file in XML format") -parser.add_argument("-i", "--infile", required=True, help="input data in XML format") -parser.add_argument("-o", "--outdir", help="Output Directory (defaults to $PWD)") -args = parser.parse_args() - - -# if verbose, dump to the terminal as well as the logfile. -if args.verbose is not None: - logging.basicConfig( - level=logging.DEBUG, - format=("%(asctime)s - %(name)s - %(levelname)s - %(message)s"), - datefmt="%y-%m-%d %H:%M:%S", - stream=sys.stdout, - ) - -# Get the basename without the suffix -xform = args.xform.replace(".xml", "") -xinst = args.infile.replace(".xml", "") - -# This is the output file in OSM format -osmfile = OsmFile(filespec=xinst + ".osm") -osmfile.header() - -# Read in the XML config file from ODK Collect -odkform = ODKForm() -odkform.parse(xform + ".xml") - -# Read the YAML config file for this XForm -yaml = Convert(xform + ".yaml") -yaml.dump() - -# Read the data instance -inst = ODKInstance() -data = inst.parse(args.infile) - -out = "" -groups = dict() -tags = dict() -node = dict() -way = dict() -for x in data: - # print(data[x]) - lat = "" - lon = "" - reg = re.compile("group*") - # There should be only one timestamp we want, namely 'end' - if odkform.getNodeType(x) == "dateTime": - dt = data[x][: data[x].find(".")] - timestamp = datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S") - groups["timestamp"] = timestamp - node["timestamp"] = timestamp - continue - elif reg.match(x): - groups[x] = data[x] - for key, _value in data[x].items(): - if odkform.getNodeType(x, key) == "geotrace": - ln = "LINESTRING(" - ln += groups[x][key] + ")" - groups[x][key] = ln - if odkform.getNodeType(x, key) == "geoshape": - # print(odkform.getNodeType(x, key)) - py = "POLYGON(" - py += groups[x][key] + ")" - groups[x][key] = py - elif odkform.getNodeType(x, key) == "geopoint": - # print(odkform.getNodeType(x, key)) - tmp = data[x][key].split(" ") - lat = tmp[0] - lon = tmp[1] - node["lat"] = tmp[0] - node["lon"] = tmp[1] - elif odkform.getNodeType(x, key) == "list": - space = data[x][key].find(" ") - if space <= 0: - values = yaml.getKeyword(data[x][key]) - tags[key] = values - else: - for item in data[x][key].split(" "): - if key == "name" or key == "alt_name": - tags[key] = data[x][key] - else: - values = yaml.getKeyword(item) - if key in tags: - tags[key] = tags[key] + ";" + item - else: - tags[key] = values - elif odkform.getNodeType(x, key) == "image": - pass - elif odkform.getNodeType(x, key) == "binary": - pass - elif odkform.getNodeType(x, key) == "int": - if data[x][key] is not None: - tags[key] = data[x][key] - elif odkform.getNodeType(x, key) == "string": - if data[x][key] is not None: - tags[key] = data[x][key] - # else: - # tags[x] = key - -node["lat"] = lat -node["lon"] = lon -node["tags"] = tags -print("FOO: %r" % node) -out += osmfile.createNode(node) - -# We're done -osmfile.write(out) -osmfile.footer() - -print("Output file: %s" % xinst + ".osm") -# print(groups) diff --git a/osm_fieldwork/ODKForm.py b/osm_fieldwork/ODKForm.py deleted file mode 100755 index 4137f017b..000000000 --- a/osm_fieldwork/ODKForm.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/python3 - -# -# Copyright (C) 2020, 2021, 2022 Humanitarian OpenstreetMap Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# - -import argparse -import logging -import os -import sys - -# Instantiate logger -log = logging.getLogger(__name__) - - -class ODKForm(object): - """Support for parsing an XLS Form, currently a work in progress...""" - - def __init__(self): - """Returns: - (ODKForm): An instance of this object. - """ - self.fields = dict() - self.nodesets = dict() - self.groups = dict() - self.ignore = ("label", "@appearance", "hint", "upload") - - def parseSelect( - self, - select: dict, - ): - """Parse a select statement in XML. - - Args: - select (dict): The select in XML: - - Returns: - (dict): The data from the select - """ - print("parseSelect %r" % type(select)) - newsel = dict() - if "item" in select: - data = self.parseItems(select["item"]) - ref = os.path.basename(select["@ref"]) - for key in data: - if key in self.ignore: - continue - newsel[ref] = data - print("\tQQQQQ %r" % (newsel)) - return newsel - - def parseItems( - self, - items: list, - ): - """Parse the items in a select list. - - Args: - items (list): The select items list in XML: - - Returns: - (dict): The data from the list of items - """ - print("\tparseItems: %r: %r" % (type(items), items)) - newitems = list() - # if type(items) == OrderedDict: - # data = list() - # data.append(items) - # else: - # data = items - - for values in items: - newitems.append(values["value"]) - - # if type(values) == str: - # continue - - # val = values['label']['@ref'].replace("/data/", "") - # tmp = val.split('/') - # group = tmp[0].replace("jr:itext(\'", "") - # fields = len(tmp) - # if fields > 2: - # subgroup = tmp[1] - # label = tmp[2].replace(":label\')", "") - # else: - # subgroup = None - # label = tmp[1].replace(":label\')", "") - # # print("VALUES: %r / %r / %r" % (group, subgroup, label)) - # if subgroup not in newdata: - # newdata[subgroup] = list() - # #newdata[subgroup].append(label) - # newitems.append(label) - # return group, subgroup, newitems - return newitems - - def parseGroup( - self, - group: dict(), - ): - """Convert the XML of a group into a data structure. - - Args: - group (dict): The group data - """ - print("\tparseGroup %r" % (type(group))) - if type(group) == list: - for _val in group: - for k in group: - print("\nZZZZ1 %r" % (k)) - else: # it's a list - for keyword, data in group.items(): - # FIXME: for now,. ignore media files - if keyword in self.ignore: - continue - print("WWW3 %r, %r" % (keyword, type(data))) - # pat = re.compile('select[0-9]*') - # if pat.match(keyword): - if keyword[0:6] == "select": - print("WWW4 select") - self.parseSelect(data) - # self.groups[] = - - -if __name__ == "__main__": - """This is just a hook so this file can be run standlone during development.""" - parser = argparse.ArgumentParser(description="convert CSV from ODK Central to OSM XML") - parser.add_argument("-v", "--verbose", action="store_true", help="verbose output") - parser.add_argument("-i", "--infile", help="The input file downloaded from ODK Central") - args = parser.parse_args() - - # if verbose, dump to the terminal as well as the logfile. - if args.verbose is not None: - logging.basicConfig( - level=logging.DEBUG, - format=("%(asctime)s - %(name)s - %(levelname)s - %(message)s"), - datefmt="%y-%m-%d %H:%M:%S", - stream=sys.stdout, - ) - - odkform = ODKForm() - odkform.parse(args.infile) - - print("---------------------------") - odkform.dump() - print("---------------------------") diff --git a/osm_fieldwork/ODKInstance.py b/osm_fieldwork/ODKInstance.py deleted file mode 100755 index 626d31340..000000000 --- a/osm_fieldwork/ODKInstance.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/python3 - -# -# Copyright (C) 2020, 2021, 2022 Humanitarian OpenstreetMap Team -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# - -import argparse -import json -import logging -import os -import re -import sys - -import flatdict -import xmltodict - -# Instantiate logger -log = logging.getLogger(__name__) - - -class ODKInstance(object): - def __init__( - self, - filespec: str = None, - data: str = None, - ): - """This class imports a ODK Instance file, which is in XML into a - data structure. - - Args: - filespec (str): The filespec to the ODK XML Instance file - data (str): The XML data - - Returns: - (ODKInstance): An instance of this object - """ - self.data = data - self.filespec = filespec - self.ignore = ["today", "start", "deviceid", "nodel", "instanceID"] - if filespec: - self.data = self.parse(filespec=filespec) - elif data: - self.data = self.parse(data) - - def parse( - self, - filespec: str, - data: str = None, - ) -> dict: - """Import an ODK XML Instance file ito a data structure. The input is - either a filespec to the Instance file copied off your phone, or - the XML that has been read in elsewhere. - - Args: - filespec (str): The filespec to the ODK XML Instance file - data (str): The XML data - - Returns: - (dict): All the entries in the OSM XML Instance file - """ - row = dict() - if filespec: - logging.info("Processing instance file: %s" % filespec) - file = open(filespec, "rb") - # Instances are small, read the whole file - xml = file.read(os.path.getsize(filespec)) - elif data: - xml = data - doc = xmltodict.parse(xml) - - json.dumps(doc) - tags = dict() - data = doc["data"] - flattened = flatdict.FlatDict(data) - rows = list() - pat = re.compile("[0-9.]* [0-9.-]* [0-9.]* [0-9.]*") - for key, value in flattened.items(): - if key[0] == "@" or value is None: - continue - if re.search(pat, value): - gps = value.split(" ") - row["lat"] = gps[0] - row["lon"] = gps[1] - continue - - # print(key, value) - tmp = key.split(":") - if tmp[len(tmp) - 1] in self.ignore: - continue - row[tmp[len(tmp) - 1]] = value - - return row - - -if __name__ == "__main__": - """This is just a hook so this file can be run standlone during development.""" - parser = argparse.ArgumentParser() - parser.add_argument("-v", "--verbose", nargs="?", const="0", help="verbose output") - parser.add_argument("-i", "--infile", required=True, help="instance data in XML format") - args = parser.parse_args() - - os.path.basename(args.infile) - - # if verbose, dump to the terminal as well as the logfile. - if args.verbose is not None: - logging.basicConfig( - level=logging.DEBUG, - format=("%(asctime)s - %(name)s - %(levelname)s - %(message)s"), - datefmt="%y-%m-%d %H:%M:%S", - stream=sys.stdout, - ) - - if not args.infile: - parser.print_help() - quit() - - inst = ODKInstance(args.infile) - data = inst.parse(args.infile) - # print(data) diff --git a/osm_fieldwork/osm2favorities.py b/osm_fieldwork/osm2favorities.py deleted file mode 100755 index 8222a336e..000000000 --- a/osm_fieldwork/osm2favorities.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/python3 - -# Copyright (c) 2023 Humanitarian OpenStreetMap Team -# -# This file is part of OSM-Fieldwork. -# -# OSM-Fieldwork is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# OSM-Fieldwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with OSM-Fieldwork. If not, see . -# - -import argparse -import logging -import os -import sys - -import geojson -import gpxpy -import gpxpy.gpx -import shapely -from lxml import etree -from shapely.geometry import shape - -# Instantiate logger -log_level = os.getenv("LOG_LEVEL", default="INFO") -# set log level for urlib -log_stream = sys.stderr # default log stream -log = logging.getLogger(__name__) - - -def createExtension(icon): - """Create an extended feature with Osmand styling.""" - # camp_pitch.png, tourism_camp_site.png, topo_camp_pitch.png, topo_camp_site.png - # trailhead.png, tourism_picnic_site.png, tourism_picnic_site.png, - # tourism_attraction.png, tourism_information.png, information_board.png, - # firepit.png, historic_ruins.png, amenity_drinking_water.png, - # amenity_toilets.png, amenity_parking.png, special_trekking - colors = {"tourism_camp_site": "#ff5020", "tourism_picnic_site": "#ff5020", "special_trekking": "#a71de1"} - nsmap = {"osmand": "https://osmand.net"} - png = etree.Element("{osmand}icon", nsmap=nsmap) - png.text = icon - back = etree.Element("{osmand}background", nsmap=nsmap) - back.text = "circle" - color = None - if icon in colors: - color = etree.Element("{osmand}color", nsmap=nsmap) - color.text = colors[icon] - return (png, back, color) - else: - return (png, back) - - -def main(): - """This main function lets this class be run standalone by a bash script.""" - parser = argparse.ArgumentParser(description="convert GeoJson to a GPX favorites file for Osmand") - parser.add_argument("-v", "--verbose", action="store_true", help="verbose output") - parser.add_argument("-i", "--infile", required=True, help="The data extract") - args = parser.parse_args() - - # if verbose, dump to the terminal. - if args.verbose is not None: - logging.basicConfig( - level=logging.DEBUG, - format=("%(threadName)10s - %(name)s - %(levelname)s - %(message)s"), - datefmt="%y-%m-%d %H:%M:%S", - stream=sys.stdout, - ) - logging.getLogger("urllib3").setLevel(logging.DEBUG) - - infile = open(args.infile, "r") - indata = geojson.load(infile) - - tourism = None - highway = None - amenity = None - # gpxpy.gpxfield.GPXField() - gpx = gpxpy.gpx.GPX() - gpx.nsmap["osmand"] = "https://osmand.net" - gpx.creator = "osm2favorites 0.1" - for feature in indata["features"]: - coords = feature["geometry"]["coordinates"] - if feature["geometry"]["type"] == "Polygon": - wkt = shape(feature["geometry"]) - center = shapely.centroid(wkt) - lat = center.y - lon = center.x - else: - lat = coords[1] - lon = coords[0] - name = "" - if "name" in feature["properties"]: - name = feature["properties"]["name"] - tourism = None - if "tourism" in feature["properties"]: - tourism = feature["properties"]["tourism"] - highway = None - if "highway" in feature["properties"]: - highway = feature["properties"]["highway"] - amenity = None - if "amenity" in feature["properties"] and not highway: - amenity = feature["properties"]["amenity"] - for key, value in feature["properties"].items(): - if key == "name": - continue - description = "

" - description += f"{key} = {value}
" - description += "

" - way = gpxpy.gpx.GPXWaypoint( - latitude=lat, - longitude=lon, - description=description, - name=name, - # symbol="", - # comment="", - ) - extensions = "" - if tourism and tourism != "picnic site": - extensions = createExtension("tourism_camp_site") - elif tourism and tourism != "picnic site": - extensions = createExtension("tourism_picnic_site") - elif highway and highway == "trailhead": - extensions = createExtension("special_trekking") - elif amenity and amenity == "parking": - extensions = createExtension("amenity_parking") - - for ext in extensions: - way.extensions.append(ext) - gpx.waypoints.append(way) - outfile = "output.gpx" - with open(outfile, "w") as f: - f.write(gpx.to_xml()) - - log.info(f"Wrote {outfile}") - - -if __name__ == "__main__": - """This is just a hook so this file can be run standlone during development.""" - main()