├── .gitignore ├── .readthedocs.yaml ├── LICENSE ├── README.md ├── build └── lib │ └── sartopo_python │ ├── __init__.py │ └── sartopo_python.py ├── dist ├── sartopo_python-1.0.2-py3-none-any.whl ├── sartopo_python-1.0.2.tar.gz ├── sartopo_python-1.0.3-py3-none-any.whl ├── sartopo_python-1.0.3.tar.gz ├── sartopo_python-1.0.4-py3-none-any.whl ├── sartopo_python-1.0.4.tar.gz ├── sartopo_python-1.0.5-py3-none-any.whl ├── sartopo_python-1.0.5.tar.gz ├── sartopo_python-1.0.6-py3-none-any.whl ├── sartopo_python-1.0.6.tar.gz ├── sartopo_python-1.1.0-py3-none-any.whl ├── sartopo_python-1.1.0.tar.gz ├── sartopo_python-1.1.1-py3-none-any.whl ├── sartopo_python-1.1.1.tar.gz ├── sartopo_python-1.1.2-py3-none-any.whl └── sartopo_python-1.1.2.tar.gz ├── docs ├── Makefile ├── build │ └── html │ │ ├── .buildinfo │ │ ├── .doctrees │ │ ├── credentials.doctree │ │ ├── environment.pickle │ │ ├── fixedChoices.doctree │ │ ├── index.doctree │ │ ├── migration.doctree │ │ ├── modules.doctree │ │ └── sartopo_python.doctree │ │ ├── _sources │ │ ├── credentials.rst.txt │ │ ├── fixedChoices.rst.txt │ │ ├── index.rst.txt │ │ ├── migration.rst.txt │ │ ├── modules.rst.txt │ │ └── sartopo_python.rst.txt │ │ ├── _static │ │ ├── alabaster.css │ │ ├── basic.css │ │ ├── caltopo_python_logo.png │ │ ├── custom.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ └── sphinx_highlight.js │ │ ├── credentials.html │ │ ├── fixedChoices.html │ │ ├── genindex.html │ │ ├── index.html │ │ ├── migration.html │ │ ├── modules.html │ │ ├── objects.inv │ │ ├── sartopo_python.html │ │ ├── search.html │ │ └── searchindex.js ├── make.bat └── source │ ├── _static │ └── caltopo_python_logo.png │ ├── _templates │ ├── allpages.html │ └── localtoc.html │ ├── conf.py │ ├── credentials.rst │ ├── fixedChoices.rst │ ├── index.rst │ ├── migration.rst │ └── sartopo_python.rst ├── requirements.txt ├── sartopo_python.egg-info ├── PKG-INFO ├── SOURCES.txt ├── dependency_links.txt ├── requires.txt └── top_level.txt ├── sartopo_python ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ └── sartopo_python.cpython-37.pyc └── sartopo_python.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .project 3 | .pydevproject 4 | sartopo_python/test.py 5 | *.pyc 6 | sartopo_python/sartopo_bg.py 7 | sartopo_python/__pycache__/sartopo_python.cpython-37.pyc 8 | *.pyc 9 | docs/build 10 | sartopo_python/test2.py 11 | sartopo_python/test_cases 12 | sartopo_python/*test 13 | *_cache.cache* 14 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the OS, Python version and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.12" 13 | # You can also specify other tool versions: 14 | # nodejs: "19" 15 | # rust: "1.64" 16 | # golang: "1.19" 17 | 18 | # Build documentation in the "docs/" directory with Sphinx 19 | sphinx: 20 | configuration: docs/source/conf.py 21 | 22 | # Optionally build your docs in additional formats such as PDF and ePub 23 | # formats: 24 | # - pdf 25 | # - epub 26 | 27 | # Optional but recommended, declare the Python requirements required 28 | # to build your documentation 29 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 30 | python: 31 | install: 32 | - requirements: requirements.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # sartopo_python 4 | 5 | See this project's [ReadTheDocs page](https://sartopo-python.readthedocs.io/en/latest/index.html). 6 | 7 | **sartopo_python is being replaced by caltopo_python.** We suggest that you change to caltopo_python as soon as possible. If you are just getting started with sartopo_python, we suggest that you use caltopo_python instead. For details, see the [ReadTheDocs Migration page](https://sartopo-python.readthedocs.io/en/latest/migration.html). 8 | 9 | CalTopo is a very popular web-browser-based and smartphone-app-based mapping tool. SARTopo is a mostly-obsolete name that refers to a set of Search-And-Rescue-specific features inside the CalTopo tool. See [caltopo.com](https://caltopo.com) and [training.caltopo.com](https://training.caltopo.com). 10 | 11 | Being a web-based tool, CalTopo uses a web API to accomplish most user actions. The API is not currently documented or developed for general public use, and could change at any time. 12 | 13 | This module provides a ‘session’ object which manages a data connection to a hosted map, and provides several wrapper methods and convenience methods that make calls to the non-publicized CalTopo API. 14 | 15 | If your CalTopo account is a member of a SAR team account, you can use this module for SAR-specific features on a caltopo.com map. 16 | 17 | **This third-party module is not written or maintained by CalTopo LLC or the authors of caltopo.com.** 18 | 19 | **DISCLAIMER: This module can edit and delete CalTopo map features. At the time of this module’s publication, CalTopo does not have any ‘undo’ capability.** Only you can take steps to prevent loss of map data due to use of this module - whether due to accidental misuse, or due to an unexpected bug in the module. You should always consider exporting a full GeoJSON from your map before using this code. 20 | 21 | ## Categories of provided class methods: 22 | - account data access 23 | - feature creation 24 | - feature editing 25 | - feature querying 26 | - feature deletion 27 | - geometry operations 28 | 29 | ## Installation 30 | Install this package in the usual manner: 31 | ``` 32 | pip install sartopo_python 33 | ``` 34 | 35 | To activate online requests, you will need to determine your account ID, credential ID, and public key. See details at the [ReadTheDocs Credentials page](https://sartopo-python.readthedocs.io/en/latest/credentials.html). 36 | 37 | -------------------------------------------------------------------------------- /build/lib/sartopo_python/__init__.py: -------------------------------------------------------------------------------- 1 | from sartopo_python.sartopo_python import SartopoSession -------------------------------------------------------------------------------- /build/lib/sartopo_python/sartopo_python.py: -------------------------------------------------------------------------------- 1 | # ############################################################################# 2 | # 3 | # sartopo_python.py - python interfaces to the sartopo API 4 | # 5 | # developed for Nevada County Sheriff's Search and Rescue 6 | # Copyright (c) 2020 Tom Grundy 7 | # 8 | # Sartopo / Caltopo currently does not have a publicly available API; 9 | # this code calls the non-publicized API that could change at any time. 10 | # 11 | # This module is intended to provide a simple, API-version-agnostic sartopo 12 | # interface to other applications. 13 | # 14 | # This python code is in no way supported or maintained by caltopo LLC 15 | # or the authors of caltopo.com or sartopo.com. 16 | # 17 | # www.github.com/ncssar/sartopo_python 18 | # 19 | # Contact the author at nccaves@yahoo.com 20 | # Attribution, feedback, bug reports and feature requests are appreciated 21 | # 22 | ############################################################ 23 | # 24 | # EXAMPLES: 25 | # 26 | # from sartopo_python import SartopoSession 27 | # import time 28 | # 29 | # sts=SartopoSession("localhost:8080","") 30 | # fid=sts.addFolder("MyFolder") 31 | # sts.addMarker(39,-120,"stuff") 32 | # sts.addMarker(39.01,-120.01,"myStuff",folderId=fid) 33 | # r=sts.getFeatures("Marker") 34 | # print("r:"+str(r)) 35 | # print("moving the marker after a pause:"+r[0]['id']) 36 | # time.sleep(5) 37 | # sts.addMarker(39.02,-120.02,r[0]['properties']['title'],existingId=r[0]['id']) 38 | # 39 | # sts2=SartopoSession( 40 | # "sartopo.com", 41 | # "", 42 | # configpath="../../sts.ini", 43 | # account="") 44 | # fid2=sts2.addFolder("MyOnlineFolder") 45 | # sts2.addMarker(39,-120,"onlineStuff") 46 | # sts2.addMarker(39.01,-119.99,"onlineStuff2",folderId=fid2) 47 | # r2=sts2.getFeatures("Marker") 48 | # print("return value from getFeatures('Marker'):") 49 | # print(json.dumps(r2,indent=3)) 50 | # time.sleep(15) 51 | # print("moving online after a pause:"+r2[0]['id']) 52 | # sts2.addMarker(39.02,-119.98,r2[0]['properties']['title'],existingId=r2[0]['id']) 53 | # 54 | # REVISION HISTORY 55 | #----------------------------------------------------------------------------- 56 | # DATE | AUTHOR | NOTES 57 | #----------------------------------------------------------------------------- 58 | # 8-29-18 TMG First version - creates folders and markers 59 | # 10-7-18 TMG allow session on network other than localhost; allow 60 | # three-character mapID; overhaul to work with 61 | # significant api changes in v4151 of sar.jar - 62 | # probably not backwards compatible; will require 63 | # changes to code that calls these functions 64 | # 11-19-18 TMG clean up for first package release 65 | # 6-29-19 TMG add getFeatures to return a list of map features with IDs; 66 | # move an existing marker by specifying existing marker ID 67 | # 7-3-19 TMG return folderId, if it exists, with each feature returned 68 | # by getFeatures, to allow filtering by folder; modify 69 | # setupSession to only return API version 1 if the 70 | # API is version 1 AND the map ID is valid 71 | # 3-30-20 TMG fix #3: v1.0.6: change the return value structure from getFeatures 72 | # to return the entire json structure for each feature; 73 | # this enables preservation of marker-symbol when moving 74 | # an existing marker 75 | # 5-30-20 TMG fix #2: v1.1.0: send signed requests to sartopo.com (online) 76 | # 6-2-20 TMG v1.1.1: fix #5 (use correct meaning of 'expires'); 77 | # fix #6 (__init__ returns None on failure) 78 | # 79 | #----------------------------------------------------------------------------- 80 | 81 | import hmac 82 | import base64 83 | import requests 84 | import json 85 | import configparser 86 | import os 87 | import time 88 | 89 | class SartopoSession(): 90 | def __init__(self,domainAndPort="localhost:8080",mapID=None,configpath=None,account=None,id=None,key=None): 91 | self.s=requests.session() 92 | self.apiVersion=-1 93 | if not mapID or not isinstance(mapID,str) or len(mapID)<3: 94 | print("ERROR: you must specify a three-or-more-character sartopo map ID string (end of the URL) when opening a SartopoSession object.") 95 | return None 96 | self.mapID=mapID 97 | self.domainAndPort=domainAndPort 98 | # configpath, account, id, and key are used to build 99 | # signed requests for sartopo.com 100 | self.configpath=configpath 101 | self.account=account 102 | self.id=id 103 | self.key=key 104 | self.setupSession() 105 | 106 | def setupSession(self): 107 | if "sartopo.com" in self.domainAndPort.lower(): 108 | id=None 109 | key=None 110 | # if configpath and account are specified, 111 | # conigpath must be the full pathname of a configparser-compliant 112 | # config file, and account must be the name of a section within it, 113 | # containing keys 'id' and 'key'. 114 | # otherwise, those parameters must have been specified in this object's 115 | # constructor. 116 | # if both are specified, first the config section is read and then 117 | # any parameters of this object are used to override the config file 118 | # values. 119 | # if any of those three values are still not specified, abort. 120 | if self.configpath is not None: 121 | if os.path.isfile(self.configpath): 122 | if self.account is None: 123 | print("config file '"+self.configpath+"' is specified, but no account name is specified.") 124 | return -1 125 | config=configparser.ConfigParser() 126 | config.read(self.configpath) 127 | if self.account not in config.sections(): 128 | print("specified account '"+self.account+"' has no entry in config file '"+self.configpath+"'.") 129 | return -1 130 | section=config[self.account] 131 | id=section.get("id",None) 132 | key=section.get("key",None) 133 | if id is None or key is None: 134 | print("account entry '"+self.account+"' in config file '"+self.configpath+"' is not complete:\n it must specify id and key.") 135 | return -1 136 | else: 137 | print("specified config file '"+self.configpath+"' does not exist.") 138 | return -1 139 | 140 | # now allow values specified in constructor to override config file values 141 | if self.id is not None: 142 | id=self.id 143 | if self.key is not None: 144 | key=self.key 145 | # finally, save them back as parameters of this object 146 | self.id=id 147 | self.key=key 148 | 149 | if self.id is None: 150 | print("sartopo session is invalid: 'id' must be specified for online maps") 151 | return -1 152 | if self.key is None: 153 | print("sartopo session is invalid: 'key' must be specified for online maps") 154 | return -1 155 | 156 | # by default, do not assume any sartopo session is running; 157 | # send a GET request to http://localhost:8080/api/v1/map/ 158 | # response code 200 = new API 159 | # otherwise: 160 | # send a GET request to http://localhost:8080/rest/ 161 | # response code 200 = old API 162 | 163 | self.apiUrlMid="/invalid/" 164 | url="http://"+self.domainAndPort+"/api/v1/map/" 165 | print("searching for API v1: sending get to "+url) 166 | try: 167 | r=self.s.get(url,timeout=2) 168 | except: 169 | print("no response from first get request; aborting; should get a response of 400 at this point for api v0") 170 | else: 171 | print("response code = "+str(r.status_code)) 172 | if r.status_code==200: 173 | # now validate the mapID, since the initial test doesn't care about mapID 174 | mapUrl="http://"+self.domainAndPort+"/m/"+self.mapID 175 | try: 176 | r=self.s.get(mapUrl,timeout=2) 177 | except: 178 | print("API version 1 detected, but the mapID is not valid.") 179 | else: 180 | if r.status_code==200: 181 | # now we know the API is valid and the mapID is valid 182 | self.apiVersion=1 183 | self.apiUrlMid="/api/v1/map/[MAPID]/" 184 | else: 185 | print("API version 1 detected, but the map-specific URL returned a code of "+str(r.status_code)+" so this session is not valid.") 186 | else: 187 | url="http://"+self.domainAndPort+"/rest/marker/" 188 | print("searching for API v0: sending get to "+url) 189 | try: 190 | r=self.s.get(url,timeout=2) 191 | except: 192 | print("no response from second get request") 193 | else: 194 | print("response code = "+str(r.status_code)) 195 | if r.status_code==200: 196 | self.apiVersion=0 197 | self.apiUrlMid="/rest/" 198 | # for v0, send a get to the map URL to authenticate the session 199 | url="http://"+self.domainAndPort+"/m/"+self.mapID 200 | print("sending API v0 authentication request to url "+url) 201 | try: 202 | r=self.s.get(url,timeout=2) 203 | except: 204 | print("no response during authentication for API v0") 205 | else: 206 | print("response code = "+str(r.status_code)) 207 | if r.status_code==200: 208 | print("API v0 session is now authenticated") 209 | print("API version:"+str(self.apiVersion)) 210 | 211 | def sendRequest(self,type,apiUrlEnd,j,id="",returnJson=None): 212 | if self.apiVersion<0: 213 | print("sartopo session is invalid; request aborted: type="+str(type)+" apiUrlEnd="+str(apiUrlEnd)) 214 | return -1 215 | apiUrlEnd=apiUrlEnd.lower() 216 | if self.apiVersion>0: 217 | apiUrlEnd=apiUrlEnd.capitalize() 218 | if apiUrlEnd.startswith("Since"): # 'since' must be lowercase even in API v1 219 | apiUrlEnd=apiUrlEnd.lower() 220 | # append id (if any) to apiUrlEnd so that it is a part of the request 221 | # destination and also a part of the pre-hased data for signed requests 222 | if id!="": # sending online request with slash at the end causes failure 223 | apiUrlEnd=apiUrlEnd+"/"+id 224 | mid=self.apiUrlMid.replace("[MAPID]",self.mapID) 225 | url="http://"+self.domainAndPort+mid+apiUrlEnd 226 | # print("sending "+str(type)+" to "+url) 227 | if type is "post": 228 | params={} 229 | params["json"]=json.dumps(j) 230 | if "sartopo.com" in self.domainAndPort.lower(): 231 | expires=int(time.time()*1000)+120000 # 2 minutes from current time, in milliseconds 232 | data="POST "+mid+apiUrlEnd+"\n"+str(expires)+"\n"+json.dumps(j) 233 | # print("pre-hashed data:"+data) 234 | token=hmac.new(base64.b64decode(self.key),data.encode(),'sha256').digest() 235 | token=base64.b64encode(token).decode() 236 | # print("hashed data:"+str(token)) 237 | params["id"]=self.id 238 | params["expires"]=expires 239 | params["signature"]=token 240 | # print("SENDING POST to '"+url+"':") 241 | # print(json.dumps(params,indent=3)) 242 | r=self.s.post(url,data=params,timeout=2) 243 | elif type is "get": # no need for json in GET; sending null JSON causes downstream error 244 | # print("SENDING GET to '"+url+"':") 245 | r=self.s.get(url,timeout=2) 246 | else: 247 | print("Unrecognized request type:"+str(type)) 248 | return -1 249 | # print("response code = "+str(r.status_code)) 250 | # print("response:") 251 | # try: 252 | # print(json.dumps(r.json(),indent=3)) 253 | # except: 254 | # print(r.text) 255 | if returnJson: 256 | try: 257 | rj=r.json() 258 | except: 259 | print("response had no decodable json") 260 | return -1 261 | else: 262 | if returnJson=="ID": 263 | id=None 264 | if 'result' in rj and 'id' in rj['result']: 265 | id=rj['result']['id'] 266 | elif 'id' in rj: 267 | id=rj['id'] 268 | else: 269 | print("No valid ID was returned from the request:") 270 | print(json.dumps(rj,indent=3)) 271 | return id 272 | if returnJson=="ALL": 273 | return rj 274 | 275 | def addFolder(self,label="New Folder"): 276 | j={} 277 | j['properties']={} 278 | j['properties']['title']=label 279 | return self.sendRequest("post","folder",j,returnJson="ID") 280 | 281 | def addMarker(self,lat,lon,title="New Marker",description="",color="#FF0000",symbol="point",rotation=None,folderId=None,existingId=""): 282 | j={} 283 | jp={} 284 | jg={} 285 | jp['class']='Marker' 286 | jp['updated']=0 287 | jp['marker-color']=color 288 | jp['marker-symbol']=symbol 289 | jp['title']=title 290 | if folderId: 291 | jp['folderId']=folderId 292 | jp['description']=description 293 | jg['type']='Point' 294 | jg['coordinates']=[float(lon),float(lat)] 295 | j['properties']=jp 296 | j['geometry']=jg 297 | j['type']='Feature' 298 | if existingId: 299 | j['id']=existingId 300 | # print("sending json: "+json.dumps(j.json(),indent=3)) 301 | return self.sendRequest("post","marker",j,id=existingId,returnJson="ID") 302 | 303 | def getFeatures(self,featureClass=None,since=0): 304 | rj=self.sendRequest("get","since/"+str(since),None,returnJson="ALL") 305 | if not featureClass: 306 | return rj # if no feature class is specified, return the entire json response 307 | else: 308 | rval=[] 309 | if 'result' in rj and 'state' in rj['result'] and 'features' in rj['result']['state']: 310 | features=rj['result']['state']['features'] 311 | for feature in features: 312 | # print("FEATURE:"+str(feature)) 313 | # id=feature['id'] 314 | prop=feature['properties'] 315 | if prop['class']==featureClass: 316 | rval.append(feature) # return the entire json object 317 | # rval.append([id,prop]) # return all properties 318 | 319 | return rval 320 | 321 | -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.2-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.2-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.2.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.3-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.3-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.3.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.4-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.4-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.4.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.5-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.5-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.5.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.6-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.6-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.0.6.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.0.6.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.1.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.1.0-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.1.0.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.1.1-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.1.1-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.1.1.tar.gz -------------------------------------------------------------------------------- /dist/sartopo_python-1.1.2-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.1.2-py3-none-any.whl -------------------------------------------------------------------------------- /dist/sartopo_python-1.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/dist/sartopo_python-1.1.2.tar.gz -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 1b5cf036bc919299b9abccaef40458d1 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/build/html/.doctrees/credentials.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/credentials.doctree -------------------------------------------------------------------------------- /docs/build/html/.doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/build/html/.doctrees/fixedChoices.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/fixedChoices.doctree -------------------------------------------------------------------------------- /docs/build/html/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/index.doctree -------------------------------------------------------------------------------- /docs/build/html/.doctrees/migration.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/migration.doctree -------------------------------------------------------------------------------- /docs/build/html/.doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/build/html/.doctrees/sartopo_python.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/.doctrees/sartopo_python.doctree -------------------------------------------------------------------------------- /docs/build/html/_sources/credentials.rst.txt: -------------------------------------------------------------------------------- 1 | :tocdepth: 1 2 | 3 | Credentials 4 | =========== 5 | 6 | To activate online requests, you need to determine these values: 7 | 8 | - account ID (6 characters) 9 | - credential ID (12 characters) 10 | - public key (44 characters) 11 | 12 | You can place these values in the configuration file, or you can specify them as arguments during session initialization. 13 | See examples at the bottom of this page. 14 | 15 | This section only refers to your CalTopo account credentials - it does not refer to your external account provider credentials 16 | (Google, Yahoo, MSN, Apple, etc.). **This module does not need credentials for your external account provider.** 17 | 18 | Your CalTopo account may have multiple sets of credentials. These show up in the 'Credentials' section at the bottom 19 | of the 'Your Account' dialog. 20 | 21 | To open the 'Your Account' dialog, sign in to caltopo.com then click your login ID name, to the right of 22 | 'Your Data' near the top right of the web interface. Don't worry if no credentials are listed yet. 23 | 24 | Each credential has a 'credential ID' (the 12-character code shown in the Credentials table), 25 | and a 'public key', which takes a bit more work to find. 26 | 27 | Currently, the public key is most easily determined during the process of creating a new credential. 28 | 29 | To create a new credential and to determine its credential ID and public key, follow these steps (based on the README at |sme-sartopo-mapsrv_link|): 30 | 31 | 1. Open a web page to caltopo.com. Make sure you are signed in to your account: 32 | you should see your user name or login name at the top right, to the right of 'Your Data'. 33 | 34 | 2. In a separate browser tab, go to |activation-link|. 35 | This should show a web page similar to |activation-image_link| from the |desktop-installation_link| instructions. Don't click Sync Account yet. 36 | 37 | 3. Open the developer console of your browser and start monitoring network traffic. 38 | For Chrome, use F12 to open Chrome DevTools; network traffic logging should be on when you open DevTools, 39 | as indicated by a red square-in-circle near the top left, which would stop monitoring network traffic 40 | when clicked. 41 | 42 | 4. Type 'sartopo_python' or a similar name for 'Your device will be synced as'. The exact name is not important, 43 | but can help you keep track of credentials in case you have several. Afterwards, the name you enter here will 44 | show up in the Credentials section of the Your Account dialog as above. 45 | 46 | 5. Check the checkbox and click Sync Account. (This should load an error page, which is OK.) 47 | 48 | 6. In the network traffic monitor, you will see many requests. After a few seconds, you can stop or pause 49 | network traffic monitoring to make sure the important entry does not get scrolled away as more new traffic happens. 50 | 51 | 7. In the first few requests, at the top of the list, you should see a request similar to:: 52 | 53 | finish-activate?code=........&name=...... 54 | 55 | Write down or copy the 8-character value after 'code=' from that request. This is not the value to put in the 56 | configuration file; you will use it in the next step. 57 | 58 | 8. In a new browser tab, go to:: 59 | 60 | caltopo.com/api/v1/activate?code= 61 | 62 | replacing with the 8-character code from the previous step. 63 | 64 | 9. This should load a page that looks like the following (possibly all compressed into one line): 65 | 66 | .. code-block:: json 67 | 68 | { 69 | "code": "XXXXXXXXXXX", 70 | "account": { 71 | "id": "ABC123", 72 | "type": "Feature", 73 | "properties": { 74 | "subscriptionExpires": 1554760038, 75 | "subscriptionType": "pro-1", 76 | "subscriptionRenew": true, 77 | "subscriptionStatus": "active", 78 | "title": "......@example", 79 | "class": "UserAccount", 80 | "updated": 1554760038, 81 | "email": "......@example.com" 82 | } 83 | }, 84 | "key": "xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=" 85 | } 86 | 87 | 10. Enter the 12-character 'code' value as 'id' in the configuration file. Enter the 44-character value of 'key' 88 | as 'key' in the configuration file. Enter the 6-character 'id' value as 'accountId' in the configuration file:: 89 | 90 | # sartopo_python config file 91 | # This file contains credentials used to send API map requests 92 | # to sartopo.com. Protect and do not distribute these credentials. 93 | [joe@example.com] 94 | id=XXXXXXXXXXXX 95 | key=xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX= 96 | accountId=ABC123 97 | 98 | Alternately, any of these can be specified as arguments when initializing the session, which will override values 99 | from the configuration file (if any): 100 | 101 | .. code-block:: python 102 | 103 | # to use the config file: specify filename and account name 104 | sts=SartopoSession('sartopo.com', 105 | configpath='../../sts.ini', 106 | account='joe@gmail.com') 107 | 108 | # to use arguments instead of the config file: 109 | sts=SartopoSession('sartopo.com', 110 | id='XXXXXXXXXXXX', 111 | key='xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=', 112 | accountId='ABC123') 113 | 114 | # to use the config file, but use arguments to override values from the config file: 115 | sts=SartopoSession('sartopo.com', 116 | configpath='../../sts.ini', 117 | account='joe@gmail.com', 118 | id='XXXXXXXXXXXX', 119 | key='xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=', 120 | accountId='ABC123') 121 | 122 | .. |activation-link| raw:: html 123 | 124 | https://caltopo.com/app/activate/offline?redirect=localhost 125 | 126 | .. |activation-image_link| raw:: html 127 | 128 | the one used during CalTopo Desktop activation 129 | 130 | .. |desktop-installation_link| raw:: html 131 | 132 | CalTopo Desktop Installation 133 | 134 | .. |sme-sartopo-mapsrv_link| raw:: html 135 | 136 | https://github.com/elliottshane/sme-sartopo-mapsrv -------------------------------------------------------------------------------- /docs/build/html/_sources/fixedChoices.rst.txt: -------------------------------------------------------------------------------- 1 | Fixed choices 2 | ------------- 3 | For some property and argument values that are specified as strings, only certain values will be processed correctly. 4 | 5 | These fixed lists of choices are determined by caltopo.com - not by the authors of this module. The lists below are (hopefully) accurate at the time of writing, but, could change at any time. 6 | 7 | The best option is to inspect outgoing network traffic while creating or editing a feature from the web interface with the selection you want. 8 | 9 | - Marker symbol name 10 | There are well over a hundred available marker symbols. Only a few common ones are listed here. Use the technique above to find the one you want. 11 | 12 | point, c:ring, c:target1, cp, heatsource, clue 13 | 14 | - Line pattern 15 | These formatted strings are too detailed to list here. Use the technique above to find the one you want. 16 | 17 | - Feature class 18 | Shape, Marker, AppTrack, LiveTrack, Folder, MapMediaObject, OperationalPeriod, Assignment, Clue, Resource, SmsLocationRequest 19 | - NOTE: Polygons and Lines are both part of the 'Shape' feature class, but are differentiated by the 'Geometry type' of 'Polygon' vs. 'LineString'. 20 | 21 | - Assignment priority 22 | HIGH, MEDIUM, LOW 23 | 24 | - Assignment POD (Responsive, Unresponsive, Clue) 25 | HIGH, MEDIUM, LOW 26 | 27 | - Assignment status 28 | DRAFT, PREPARED, INPROGRESS, COMPLETED 29 | 30 | - Assignment resource type 31 | 32 | GROUND, GROUND_1, GROUND_2, GROUND_3, DOG, DOG_TRAIL, DOG_AREA, DOG_HRD, OHV, BIKE, WATER, MOUNTED, AIR 33 | 34 | - NOTE: Underscores are used in the actual property values, but the web interface will display hyphens instead of underscores. -------------------------------------------------------------------------------- /docs/build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | .. sartopo_python documentation master file, created by 2 | sphinx-quickstart on Fri May 17 19:27:57 2024. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | :tocdepth: 1 7 | 8 | .. .. toctree:: 9 | .. .. :maxdepth: 2 10 | .. :caption: Other pages: 11 | 12 | .. credentials 13 | .. sartopo_python 14 | 15 | sartopo_python 16 | ========================================== 17 | 18 | CalTopo is a very popular web-browser-based and smartphone-app-based mapping tool. SARTopo is a mostly-obsolete 19 | name that refers to a set of Search-And-Rescue-specific features inside the CalTopo tool. See |caltopo_link| and |training_link|. 20 | 21 | Being a web-based tool, CalTopo / SARTopo uses a web API to accomplish most user actions. The API is not currently documented or developed for general public use, and could change at any time. 22 | 23 | This module provides a 'session' object which manages a data connection to a hosted map, and provides several wrapper methods and convenience methods that make calls to the non-publicized CalTopo API. 24 | 25 | **This third-party module is not written or maintained by CalTopo LLC or the authors of caltopo.com or sartopo.com.** 26 | 27 | **DISCLAIMER: This module can edit and delete CalTopo / SARTopo map features. At the time of this module's publication, CalTopo and SARTopo do not have any 'undo' capability.** 28 | Only you can take steps to prevent loss of map data due to use of this module - whether due to accidental misuse, or due to an unexpected bug in the module. You should always consider exporting a full GeoJSON from your map before using this code. 29 | 30 | Categories of provided class methods: 31 | - account data access 32 | - feature creation 33 | - feature editing 34 | - feature querying 35 | - feature deletion 36 | - geometry operations 37 | 38 | See the `SartopoSession Class Reference <./sartopo_python.html>`_ for details. 39 | 40 | Installation 41 | ============ 42 | Install this package in the usual manner: 43 | 44 | .. code-block:: python 45 | 46 | pip install sartopo_python 47 | 48 | To activate online requests, you will need to determine your account ID, credential ID, and public key. See details at the :doc:`credentials` page. 49 | 50 | **NOTE: sartopo_python is changing names to caltopo_python.** 51 | caltopo_python 1.0.x will be identical to sartopo_python 2.0.x. 52 | 53 | We suggest that you change to caltopo_python as soon as possible. If you are just getting started with sartopo_python, we suggest that you use caltopo_python instead. 54 | 55 | For more information, see the :doc:`migration` page. 56 | 57 | Key Features 58 | =============== 59 | 60 | Internet or locally-hosted 61 | -------------------------- 62 | In addition to the standard caltopo.com or sartopo.com web interface, CalTopo provides a downloadable local HTTP server 63 | called CalTopo Desktop (formerly CalTopo Offline or SARTopo Offline). This module works with either solution. 64 | 65 | Configuration file 66 | ------------------ 67 | User account details, including the authentication public key (used to generate signed requests, as required by caltopo.com and sartopo.com), 68 | are kept in a local configuration file that you control. A template configuration file comes with this package. 69 | 70 | See the bottom of the Examples section for configuration file examples. See the :doc:`credentials` page for details on authentication. 71 | 72 | Mapless session 73 | --------------- 74 | You may want to initialize the session without specifying a map, e.g. if you need to start 75 | by checking the list of available maps before you know which map to open. 76 | 77 | You can open a 'mapless' session by simply omitting the mapID argument when you initialize the session. In that case, you can 78 | open a map later, within the same session, with .openMap(). 79 | 80 | Any of the 'account data access methods' will work in a mapless session. 81 | Most of the other class methods require an open map, so will fail with an error message if called in a mapless session. 82 | 83 | Local cache 84 | ----------- 85 | If the session is associated with a hosted map, this module will keep a local cache of the entire map data structure. This reduces 86 | the number of web requests needed, which reduces the time taken for most tasks. 87 | 88 | Sync with callbacks 89 | ------------------- 90 | To keep the local cache in sync with the hosted map, this module automatically requests and processes updates from the host at the specified sync interval. 91 | 92 | This sync procedure is done in a background thread, so that it doesn't delay or interfere with your code. Sync can be paused or disabled if needed. 93 | 94 | You can also write callback functions that will be called whenever map data changes are found during sync. 95 | 96 | Examples 97 | ======== 98 | 99 | Opening a session 100 | ----------------- 101 | 102 | .. code-block:: python 103 | 104 | from sartopo_python import SartopoSession 105 | 106 | # open an online session and map 107 | sts=SartopoSession('caltopo.com','A1B2C', 108 | configpath='../../sts.ini', 109 | account='joe@domain.com') 110 | 111 | # open a CalTopo Desktop session and map 112 | sts=SartopoSession('localhost:8080','A1B2C', 113 | configpath='../../sts.ini', 114 | account='joe@domain.com') 115 | 116 | # open an online mapless session 117 | sts=SartopoSession('caltopo.com', 118 | configpath='../../sts.ini', 119 | account='joe@domain.com') 120 | 121 | # open a map, for a session that was initially mapless 122 | sts.openMap('A1B2C') 123 | 124 | Syncing and callbacks 125 | --------------------- 126 | 127 | .. code-block:: python 128 | 129 | # define callback functions 130 | def pucb(*args): 131 | print('Property Updated: pucb called with args '+str(args)) 132 | 133 | def gucb(*args): 134 | print('Geometry Updated: gucb called with args '+str(args)) 135 | 136 | def nfcb(*args): 137 | print('New Feature: nfcb called with args '+str(args)) 138 | 139 | def dfcb(*args): 140 | print('Deleted Feature: dfcb called with args '+str(args)) 141 | 142 | # open a session, connecting to the defined callbacks; 143 | # syncing is enabled by default, since the 'sync' argument defaults to True 144 | sts=SartopoSession('caltopo.com','A1B2C', 145 | configpath='../../sts.ini', 146 | account='joe@domain.com', 147 | propUpdateCallback=pucb, 148 | geometryUpdateCallback=gucb, 149 | newFeatureCallback=nfcb, 150 | deletedFeatureCallback=dfcb) 151 | 152 | Getting map data and account data 153 | --------------------------------- 154 | 155 | .. code-block:: python 156 | 157 | # get the personal map list (for joe@domain.com) 158 | sts.getMapList() 159 | 160 | # get the MyTeam map list (assuming joe@domain.com is a member of MyTeam) 161 | sts.getMapList('MyTeam') 162 | 163 | # get a dict of all map lists (for joe@domain.com) 164 | sts.getAllMapLists() 165 | 166 | # get the title of a map (assuming joe@domain.com has access to the map) 167 | sts.getMapTitle('A1B2C') 168 | 169 | # get the list of titles of group accounts of which joe@domain.com is a member 170 | sts.getGroupAccountTitles() 171 | 172 | Adding features 173 | --------------- 174 | 175 | A word on longitude / latitude sequence: 176 | 177 | caltopo.com expects each point of every type of geometry to have longitude first, followed by latutude, e.g. [120,-39]. 178 | 179 | While the code will swap coordinates if needed and if detectable (which is only the case for half of the globe), it's best to get in the habit of 180 | specifying points in [lon,lat] sequence. See the *._validatePoints* documentation for details. 181 | 182 | This is opposite of the Marker functions, which call for the latitude argument first. 183 | 184 | .. code-block:: python 185 | 186 | # add a marker 187 | sts.addMarker(39,-120,'MyMarker') 188 | 189 | # add a folder 190 | fid=sts.addFolder('MyFolder') 191 | 192 | # add a marker in the folder 193 | myMarker2=sts.addMarker(39.01,-120.01,'MyMarker2',folderId=fid) 194 | 195 | # add a line 196 | sts.addLine([[39,-120],[39.1,-120.1]],'MyLine') 197 | 198 | # prepare to add a polygon - queue it for later 199 | sts.addPolygon([[39,-120],[39.1,-120.1],[39.1,-120]],'MyPolygon',queue=True) 200 | 201 | # add an Operational Period 202 | op1=sts.addOperationalPeriod('1') 203 | 204 | # prepare to add a line assignment - queue it for later 205 | aa=sts.addLineAssignment([[39.2,-120],[39.2,-120.1]], 206 | letter='AA', 207 | opId=op1, 208 | resourceType='DOG-TRAIL', 209 | description='FindEm', 210 | queue=True) 211 | 212 | sts.addAreaAssignment([[39.3,-120],[39.4,-120.1],[39.4,-120]], 213 | letter='AB', 214 | number='104', 215 | opId=op1, 216 | resourceType='DOG-AREA', 217 | description='FindEmFirst', 218 | responsivePOD='HIGH', 219 | priority='HIGH') 220 | 221 | # add the queued features now (MyPolygon and AA) 222 | sts.flush() 223 | 224 | Querying and editing features 225 | ----------------------------- 226 | 227 | .. code-block:: python 228 | 229 | myMarker=sts.getFeature('Marker','MyMarker') 230 | 231 | sts.editFeature(myMarker['id'],properties={'title','NewTitle'}) 232 | 233 | sts.moveMarker(39,-121.5,myMarker['id']) 234 | 235 | sts.editMarkerDescription('New marker description',myMarker['id']) 236 | 237 | Geometry operations 238 | ------------------- 239 | 240 | .. code-block:: python 241 | 242 | # assuming all of the named features below have already been drawn 243 | 244 | # cut area assignment AC 103, using line b0 245 | sts.cut('AC 103','b0') 246 | 247 | # cut line a1, using line b1 248 | sts.cut('a1','b1') 249 | 250 | # cut polygon a8, using polygon b8, but do not delete b8 afterwards 251 | sts.cut('a8','b8',deleteCutter=False) 252 | 253 | # arguments are ids instead of entire features 254 | sts.cut(a12['id'],b12['id']) 255 | 256 | # expand polygon a7 to include polygon b7, a.k.a. "a7 = a7 OR b7" 257 | sts.expand('a7','b7') 258 | 259 | # crop line a14 using boundary poygon b14 260 | sts.crop('a14','b14') 261 | 262 | # crop line a15 using boundary polygon b15, with zero oversize 263 | sts.crop('a15','b15',beyond=0) 264 | 265 | Deleting features 266 | ----------------- 267 | 268 | .. code-block:: python 269 | 270 | sts.delFeature(aa) 271 | 272 | sts.delMarkers([myMarker,myMarker2]) 273 | 274 | Configuration file 275 | ------------------ 276 | 277 | .. code-block:: python 278 | 279 | # sartopo_python config file 280 | # This file contains credentials used to send API map requests 281 | # to caltopo.com, sartopo.com, or CalTopo Desktop. 282 | # Protect and do not distribute these credentials. 283 | 284 | [joe@domain.com] # section referenced by 'account' session object attribute / argument 285 | id=A1B2C3D4E5F6 # 12-character credential ID 286 | key=............................................ # 44-character caltopo API key 287 | accountId=A1B2C3 # 6-character account ID 288 | 289 | 290 | .. Indices and tables 291 | .. ================== 292 | 293 | .. * :ref:`genindex` 294 | .. * :ref:`modindex` 295 | .. * :ref:`search` 296 | 297 | .. |caltopo_link| raw:: html 298 | 299 | caltopo.com 300 | 301 | .. |training_link| raw:: html 302 | 303 | training.caltopo.com 304 | 305 | 306 | -------------------------------------------------------------------------------- /docs/build/html/_sources/migration.rst.txt: -------------------------------------------------------------------------------- 1 | Migration 2 | ========================================== 3 | 4 | There are two categories of migration that you might be concerned with: 5 | 6 | 1. sartopo_python is changing names to caltopo_python 7 | ----------------------------------------------------- 8 | 9 | This module began as 'sartopo_python'. There are several reasons for changing the module name, including: 10 | 11 | - most of the features in this module are not SAR-specific 12 | - sartopo.com is now effectively the same as caltopo.com; the only difference is the 'SAR' vs. 'Recreation' mode setting for each map 13 | - caltopo.com has much broader name recognition than sartopo.com outside of the SAR community 14 | - CalTopo is the name of the app; there is no 'SARTopo app' 15 | - the name of the downloadble server is CalTopo Desktop (formerly SARTopo Offline or CalTopo Offline); there is no 'SARTopo Desktop' 16 | 17 | caltopo_python 1.0.x will be identical to sartopo_python 2.0.x. 18 | 19 | sartopo_python will not receive any updates after 2.0.x. That is, there will be no sartopo_python 2.1.0. 20 | Patches / bug fixes to 1.0 / 2.0 will be applied to both packages, but, 21 | minor and major version updates will only be applied to caltopo_python. 22 | 23 | We suggest that you change to caltopo_python as soon as possible. If you are just getting started with sartopo_python, we suggest that you use caltopo_python instead. 24 | 25 | *How do I migrate from sartopo_python to caltopo_python?* 26 | 27 | 1. pip install caltopo_python 28 | 2. import caltopo_python instead of import sartopo_python 29 | 3. create an instance of CaltopoSession instead of SartopoSession 30 | 31 | There is no change to class method names or signatures. The class name is the only difference. 32 | 33 | 2. migrating to sartopo_python 2.0.0 from an earlier version 34 | ------------------------------------------------------------ 35 | 36 | (or, migrating to caltopo_python 1.0.0 from an earlier version of sartopo_python) 37 | 38 | Some class method names and signatures have changed from earlier versions. 39 | 40 | Several method names now start with an underscore, to indicate that they are not likely to be needed directly 41 | in your downstream code. These 'internal' data management and helper methods are normally only called internally 42 | by other class methods. 43 | 44 | They can still be called from your downstream code if there is a specific need, as long as you are fully aware 45 | of their impacts on internal class data - especially the local cache and the threaded queueing operations. 46 | 47 | These previously-non-underscored methods from the latest PyPi version (1.1.2) are now 'internal' (e.g. .setupSession is now ._setupSession): 48 | 49 | - ._setupSession, ._sendRequest 50 | 51 | In addition, these previously-non-underscored methods from more recent versions of source code are now 'internal': 52 | 53 | - ._sendUserdata, ._doSync, ._refresh, ._start, ._stop, ._pause, ._resume, ._buffer2, ._intersection2, ._caseMatch, ._twoify, ._fourify, ._removeDuplicatePoints, ._removeSpurs, ._getUsedSuffixList, ._getNextAvailableSuffix, ._getToken 54 | 55 | In general, the changes from the latest PyPi version (1.1.2) are extensive enough that documenting 56 | the differences would not be any more helpful than looking in the Class Reference. 57 | 58 | Changes from more recent versions of source code mainly involve method signatures (argument names, types, and sequences). 59 | 60 | In either case, migrating your code from previous versions may be a fair amount of work, but the new feature set and 61 | forward compatibility should be a good payoff. Use the Examples section of the main page, 62 | and the Class Reference, as your guides. -------------------------------------------------------------------------------- /docs/build/html/_sources/modules.rst.txt: -------------------------------------------------------------------------------- 1 | sartopo_python 2 | ============== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | sartopo_python 8 | -------------------------------------------------------------------------------- /docs/build/html/_sources/sartopo_python.rst.txt: -------------------------------------------------------------------------------- 1 | :tocdepth: 3 2 | 3 | sartopo\_python module 4 | ====================== 5 | 6 | This module provides one main class: SartopoSession. Class methods are categorized and documented below. 7 | 8 | Downstream code should create and use one instance of this class. 9 | 10 | An exception class is also defined, STSException, which could be thrown during initialization but is not documented here. 11 | 12 | .. see https://stackoverflow.com/a/48682589/3577105 for categorization technique 13 | .. currentmodule:: sartopo_python 14 | 15 | .. autoclass:: SartopoSession 16 | 17 | .. omitting class name from automethod calls, when the section headers are unindented, 18 | .. causes these failures, but, the headers need to be unindented for the TOC to recognize them: 19 | .. WARNING: don't know which module to import for autodocumenting 'openMap' (try placing a "module" or "currentmodule" directive in the document, or giving an explicit module name) 20 | 21 | .. **Session setup methods** 22 | .. ------------------------- 23 | 24 | .. automethod:: SartopoSession.openMap 25 | 26 | **Account data access methods** 27 | ------------------------------- 28 | These methods may be called from either a mapless or a map-associated session. 29 | 30 | .. automethod:: SartopoSession.getAccountData 31 | .. automethod:: SartopoSession.getMapList 32 | .. automethod:: SartopoSession.getAllMapLists 33 | .. automethod:: SartopoSession.getMapTitle 34 | .. automethod:: SartopoSession.getGroupAccountTitles 35 | 36 | **Feature creation methods** 37 | ---------------------------- 38 | Most of these feature creation methods can be used to edit an existing feature, 39 | by specifying the existingId argument. .editFeature is a convenience method 40 | that calls the appropriate .add... method with existingId specified. 41 | 42 | .. automethod:: SartopoSession.addFolder 43 | .. automethod:: SartopoSession.addMarker 44 | .. automethod:: SartopoSession.addLine 45 | .. automethod:: SartopoSession.addPolygon 46 | .. automethod:: SartopoSession.addOperationalPeriod 47 | .. automethod:: SartopoSession.addLineAssignment 48 | .. automethod:: SartopoSession.addAreaAssignment 49 | .. automethod:: SartopoSession.addAppTrack 50 | .. automethod:: SartopoSession.flush 51 | 52 | **Feature query methods** 53 | ------------------------- 54 | 55 | .. automethod:: SartopoSession.getFeature 56 | .. automethod:: SartopoSession.getFeatures 57 | 58 | **Feature editing methods** 59 | --------------------------- 60 | 61 | .. automethod:: SartopoSession.editFeature 62 | .. automethod:: SartopoSession.moveMarker 63 | .. automethod:: SartopoSession.editMarkerDescription 64 | 65 | **Feature deletion methods** 66 | ---------------------------- 67 | 68 | .. automethod:: SartopoSession.delFeature 69 | .. automethod:: SartopoSession.delFeatures 70 | .. automethod:: SartopoSession.delMarker 71 | .. automethod:: SartopoSession.delMarkers 72 | 73 | **Geometry operation methods** 74 | ------------------------------ 75 | These methods use the python |shapely_link| module. 76 | 77 | .. automethod:: SartopoSession.cut 78 | .. automethod:: SartopoSession.expand 79 | .. automethod:: SartopoSession.crop 80 | .. automethod:: SartopoSession.getBounds 81 | 82 | **Internal data management methods** 83 | ------------------------------------ 84 | These methods are typically only called internally, from other class methods. They can be called from downstream code if needed, with caution. 85 | 86 | .. automethod:: SartopoSession._setupSession 87 | .. automethod:: SartopoSession._sendUserdata 88 | .. automethod:: SartopoSession._doSync 89 | .. automethod:: SartopoSession._refresh 90 | .. automethod:: SartopoSession.__del__ 91 | .. automethod:: SartopoSession._start 92 | .. automethod:: SartopoSession._stop 93 | .. automethod:: SartopoSession._pause 94 | .. automethod:: SartopoSession._resume 95 | .. automethod:: SartopoSession._syncLoop 96 | .. automethod:: SartopoSession._sendRequest 97 | .. automethod:: SartopoSession._delAsync 98 | .. automethod:: SartopoSession._buffer2 99 | .. automethod:: SartopoSession._intersection2 100 | 101 | **Internal helper methods** 102 | --------------------------- 103 | These methods are typically only called internally, from other class methods. They can be called from downstream code if needed, with caution. 104 | 105 | .. automethod:: SartopoSession._caseMatch 106 | .. automethod:: SartopoSession._twoify 107 | .. automethod:: SartopoSession._fourify 108 | .. automethod:: SartopoSession._removeDuplicatePoints 109 | .. automethod:: SartopoSession._removeSpurs 110 | .. automethod:: SartopoSession._getUsedSuffixList 111 | .. automethod:: SartopoSession._getNextAvailableSuffix 112 | .. automethod:: SartopoSession._validatePoints 113 | .. automethod:: SartopoSession._getToken 114 | 115 | .. |shapely_link| raw:: html 116 | 117 | Shapely -------------------------------------------------------------------------------- /docs/build/html/_static/alabaster.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: Georgia, serif; 7 | font-size: 17px; 8 | background-color: #fff; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | 15 | div.document { 16 | width: 85%; 17 | margin: 30px auto 0 auto; 18 | } 19 | 20 | div.documentwrapper { 21 | float: left; 22 | width: 100%; 23 | } 24 | 25 | div.bodywrapper { 26 | margin: 0 0 0 250px; 27 | } 28 | 29 | div.sphinxsidebar { 30 | width: 250px; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #fff; 41 | color: #3E4349; 42 | padding: 0 30px 0 30px; 43 | } 44 | 45 | div.body > .section { 46 | text-align: left; 47 | } 48 | 49 | div.footer { 50 | width: 85%; 51 | margin: 20px auto 30px auto; 52 | font-size: 14px; 53 | color: #888; 54 | text-align: right; 55 | } 56 | 57 | div.footer a { 58 | color: #888; 59 | } 60 | 61 | p.caption { 62 | font-family: inherit; 63 | font-size: inherit; 64 | } 65 | 66 | 67 | div.relations { 68 | display: none; 69 | } 70 | 71 | 72 | div.sphinxsidebar { 73 | max-height: 100%; 74 | overflow-y: auto; 75 | } 76 | 77 | div.sphinxsidebar a { 78 | color: #444; 79 | text-decoration: none; 80 | border-bottom: 1px dotted #999; 81 | } 82 | 83 | div.sphinxsidebar a:hover { 84 | border-bottom: 1px solid #999; 85 | } 86 | 87 | div.sphinxsidebarwrapper { 88 | padding: 18px 10px; 89 | } 90 | 91 | div.sphinxsidebarwrapper p.logo { 92 | padding: 0; 93 | margin: -10px 0 0 0px; 94 | text-align: center; 95 | } 96 | 97 | div.sphinxsidebarwrapper h1.logo { 98 | margin-top: -10px; 99 | text-align: center; 100 | margin-bottom: 5px; 101 | text-align: left; 102 | } 103 | 104 | div.sphinxsidebarwrapper h1.logo-name { 105 | margin-top: 0px; 106 | } 107 | 108 | div.sphinxsidebarwrapper p.blurb { 109 | margin-top: 0; 110 | font-style: normal; 111 | } 112 | 113 | div.sphinxsidebar h3, 114 | div.sphinxsidebar h4 { 115 | font-family: Georgia, serif; 116 | color: #444; 117 | font-size: 24px; 118 | font-weight: normal; 119 | margin: 0 0 5px 0; 120 | padding: 0; 121 | } 122 | 123 | div.sphinxsidebar h4 { 124 | font-size: 20px; 125 | } 126 | 127 | div.sphinxsidebar h3 a { 128 | color: #444; 129 | } 130 | 131 | div.sphinxsidebar p.logo a, 132 | div.sphinxsidebar h3 a, 133 | div.sphinxsidebar p.logo a:hover, 134 | div.sphinxsidebar h3 a:hover { 135 | border: none; 136 | } 137 | 138 | div.sphinxsidebar p { 139 | color: #555; 140 | margin: 10px 0; 141 | } 142 | 143 | div.sphinxsidebar ul { 144 | margin: 10px 0; 145 | padding: 0; 146 | color: #000; 147 | } 148 | 149 | div.sphinxsidebar ul li.toctree-l1 > a { 150 | font-size: 120%; 151 | } 152 | 153 | div.sphinxsidebar ul li.toctree-l2 > a { 154 | font-size: 110%; 155 | } 156 | 157 | div.sphinxsidebar input { 158 | border: 1px solid #CCC; 159 | font-family: Georgia, serif; 160 | font-size: 1em; 161 | } 162 | 163 | div.sphinxsidebar #searchbox input[type="text"] { 164 | width: 160px; 165 | } 166 | 167 | div.sphinxsidebar .search > div { 168 | display: table-cell; 169 | } 170 | 171 | div.sphinxsidebar hr { 172 | border: none; 173 | height: 1px; 174 | color: #AAA; 175 | background: #AAA; 176 | 177 | text-align: left; 178 | margin-left: 0; 179 | width: 50%; 180 | } 181 | 182 | div.sphinxsidebar .badge { 183 | border-bottom: none; 184 | } 185 | 186 | div.sphinxsidebar .badge:hover { 187 | border-bottom: none; 188 | } 189 | 190 | /* To address an issue with donation coming after search */ 191 | div.sphinxsidebar h3.donation { 192 | margin-top: 10px; 193 | } 194 | 195 | /* -- body styles ----------------------------------------------------------- */ 196 | 197 | a { 198 | color: #004B6B; 199 | text-decoration: underline; 200 | } 201 | 202 | a:hover { 203 | color: #6D4100; 204 | text-decoration: underline; 205 | } 206 | 207 | div.body h1, 208 | div.body h2, 209 | div.body h3, 210 | div.body h4, 211 | div.body h5, 212 | div.body h6 { 213 | font-family: Georgia, serif; 214 | font-weight: normal; 215 | margin: 30px 0px 10px 0px; 216 | padding: 0; 217 | } 218 | 219 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 220 | div.body h2 { font-size: 180%; } 221 | div.body h3 { font-size: 150%; } 222 | div.body h4 { font-size: 130%; } 223 | div.body h5 { font-size: 100%; } 224 | div.body h6 { font-size: 100%; } 225 | 226 | a.headerlink { 227 | color: #DDD; 228 | padding: 0 4px; 229 | text-decoration: none; 230 | } 231 | 232 | a.headerlink:hover { 233 | color: #444; 234 | background: #EAEAEA; 235 | } 236 | 237 | div.body p, div.body dd, div.body li { 238 | line-height: 1.4em; 239 | } 240 | 241 | div.admonition { 242 | margin: 20px 0px; 243 | padding: 10px 30px; 244 | background-color: #EEE; 245 | border: 1px solid #CCC; 246 | } 247 | 248 | div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { 249 | background-color: #FBFBFB; 250 | border-bottom: 1px solid #fafafa; 251 | } 252 | 253 | div.admonition p.admonition-title { 254 | font-family: Georgia, serif; 255 | font-weight: normal; 256 | font-size: 24px; 257 | margin: 0 0 10px 0; 258 | padding: 0; 259 | line-height: 1; 260 | } 261 | 262 | div.admonition p.last { 263 | margin-bottom: 0; 264 | } 265 | 266 | div.highlight { 267 | background-color: #fff; 268 | } 269 | 270 | dt:target, .highlight { 271 | background: #FAF3E8; 272 | } 273 | 274 | div.warning { 275 | background-color: #FCC; 276 | border: 1px solid #FAA; 277 | } 278 | 279 | div.danger { 280 | background-color: #FCC; 281 | border: 1px solid #FAA; 282 | -moz-box-shadow: 2px 2px 4px #D52C2C; 283 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 284 | box-shadow: 2px 2px 4px #D52C2C; 285 | } 286 | 287 | div.error { 288 | background-color: #FCC; 289 | border: 1px solid #FAA; 290 | -moz-box-shadow: 2px 2px 4px #D52C2C; 291 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 292 | box-shadow: 2px 2px 4px #D52C2C; 293 | } 294 | 295 | div.caution { 296 | background-color: #FCC; 297 | border: 1px solid #FAA; 298 | } 299 | 300 | div.attention { 301 | background-color: #FCC; 302 | border: 1px solid #FAA; 303 | } 304 | 305 | div.important { 306 | background-color: #EEE; 307 | border: 1px solid #CCC; 308 | } 309 | 310 | div.note { 311 | background-color: #EEE; 312 | border: 1px solid #CCC; 313 | } 314 | 315 | div.tip { 316 | background-color: #EEE; 317 | border: 1px solid #CCC; 318 | } 319 | 320 | div.hint { 321 | background-color: #EEE; 322 | border: 1px solid #CCC; 323 | } 324 | 325 | div.seealso { 326 | background-color: #EEE; 327 | border: 1px solid #CCC; 328 | } 329 | 330 | div.topic { 331 | background-color: #EEE; 332 | } 333 | 334 | p.admonition-title { 335 | display: inline; 336 | } 337 | 338 | p.admonition-title:after { 339 | content: ":"; 340 | } 341 | 342 | pre, tt, code { 343 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 344 | font-size: 0.9em; 345 | } 346 | 347 | .hll { 348 | background-color: #FFC; 349 | margin: 0 -12px; 350 | padding: 0 12px; 351 | display: block; 352 | } 353 | 354 | img.screenshot { 355 | } 356 | 357 | tt.descname, tt.descclassname, code.descname, code.descclassname { 358 | font-size: 0.95em; 359 | } 360 | 361 | tt.descname, code.descname { 362 | padding-right: 0.08em; 363 | } 364 | 365 | img.screenshot { 366 | -moz-box-shadow: 2px 2px 4px #EEE; 367 | -webkit-box-shadow: 2px 2px 4px #EEE; 368 | box-shadow: 2px 2px 4px #EEE; 369 | } 370 | 371 | table.docutils { 372 | border: 1px solid #888; 373 | -moz-box-shadow: 2px 2px 4px #EEE; 374 | -webkit-box-shadow: 2px 2px 4px #EEE; 375 | box-shadow: 2px 2px 4px #EEE; 376 | } 377 | 378 | table.docutils td, table.docutils th { 379 | border: 1px solid #888; 380 | padding: 0.25em 0.7em; 381 | } 382 | 383 | table.field-list, table.footnote { 384 | border: none; 385 | -moz-box-shadow: none; 386 | -webkit-box-shadow: none; 387 | box-shadow: none; 388 | } 389 | 390 | table.footnote { 391 | margin: 15px 0; 392 | width: 100%; 393 | border: 1px solid #EEE; 394 | background: #FDFDFD; 395 | font-size: 0.9em; 396 | } 397 | 398 | table.footnote + table.footnote { 399 | margin-top: -15px; 400 | border-top: none; 401 | } 402 | 403 | table.field-list th { 404 | padding: 0 0.8em 0 0; 405 | } 406 | 407 | table.field-list td { 408 | padding: 0; 409 | } 410 | 411 | table.field-list p { 412 | margin-bottom: 0.8em; 413 | } 414 | 415 | /* Cloned from 416 | * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 417 | */ 418 | .field-name { 419 | -moz-hyphens: manual; 420 | -ms-hyphens: manual; 421 | -webkit-hyphens: manual; 422 | hyphens: manual; 423 | } 424 | 425 | table.footnote td.label { 426 | width: .1px; 427 | padding: 0.3em 0 0.3em 0.5em; 428 | } 429 | 430 | table.footnote td { 431 | padding: 0.3em 0.5em; 432 | } 433 | 434 | dl { 435 | margin-left: 0; 436 | margin-right: 0; 437 | margin-top: 0; 438 | padding: 0; 439 | } 440 | 441 | dl dd { 442 | margin-left: 30px; 443 | } 444 | 445 | blockquote { 446 | margin: 0 0 0 30px; 447 | padding: 0; 448 | } 449 | 450 | ul, ol { 451 | /* Matches the 30px from the narrow-screen "li > ul" selector below */ 452 | margin: 10px 0 10px 30px; 453 | padding: 0; 454 | } 455 | 456 | pre { 457 | background: #EEE; 458 | padding: 7px 30px; 459 | margin: 15px 0px; 460 | line-height: 1.3em; 461 | } 462 | 463 | div.viewcode-block:target { 464 | background: #ffd; 465 | } 466 | 467 | dl pre, blockquote pre, li pre { 468 | margin-left: 0; 469 | padding-left: 30px; 470 | } 471 | 472 | tt, code { 473 | background-color: #ecf0f3; 474 | color: #222; 475 | /* padding: 1px 2px; */ 476 | } 477 | 478 | tt.xref, code.xref, a tt { 479 | background-color: #FBFBFB; 480 | border-bottom: 1px solid #fff; 481 | } 482 | 483 | a.reference { 484 | text-decoration: none; 485 | border-bottom: 1px dotted #004B6B; 486 | } 487 | 488 | /* Don't put an underline on images */ 489 | a.image-reference, a.image-reference:hover { 490 | border-bottom: none; 491 | } 492 | 493 | a.reference:hover { 494 | border-bottom: 1px solid #6D4100; 495 | } 496 | 497 | a.footnote-reference { 498 | text-decoration: none; 499 | font-size: 0.7em; 500 | vertical-align: top; 501 | border-bottom: 1px dotted #004B6B; 502 | } 503 | 504 | a.footnote-reference:hover { 505 | border-bottom: 1px solid #6D4100; 506 | } 507 | 508 | a:hover tt, a:hover code { 509 | background: #EEE; 510 | } 511 | 512 | 513 | @media screen and (max-width: 870px) { 514 | 515 | div.sphinxsidebar { 516 | display: none; 517 | } 518 | 519 | div.document { 520 | width: 100%; 521 | 522 | } 523 | 524 | div.documentwrapper { 525 | margin-left: 0; 526 | margin-top: 0; 527 | margin-right: 0; 528 | margin-bottom: 0; 529 | } 530 | 531 | div.bodywrapper { 532 | margin-top: 0; 533 | margin-right: 0; 534 | margin-bottom: 0; 535 | margin-left: 0; 536 | } 537 | 538 | ul { 539 | margin-left: 0; 540 | } 541 | 542 | li > ul { 543 | /* Matches the 30px from the "ul, ol" selector above */ 544 | margin-left: 30px; 545 | } 546 | 547 | .document { 548 | width: auto; 549 | } 550 | 551 | .footer { 552 | width: auto; 553 | } 554 | 555 | .bodywrapper { 556 | margin: 0; 557 | } 558 | 559 | .footer { 560 | width: auto; 561 | } 562 | 563 | .github { 564 | display: none; 565 | } 566 | 567 | 568 | 569 | } 570 | 571 | 572 | 573 | @media screen and (max-width: 875px) { 574 | 575 | body { 576 | margin: 0; 577 | padding: 20px 30px; 578 | } 579 | 580 | div.documentwrapper { 581 | float: none; 582 | background: #fff; 583 | } 584 | 585 | div.sphinxsidebar { 586 | display: block; 587 | float: none; 588 | width: 102.5%; 589 | margin: -20px -30px 20px -30px; 590 | padding: 10px 20px; 591 | background: #333; 592 | color: #FFF; 593 | } 594 | 595 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 596 | div.sphinxsidebar h3 a { 597 | color: #fff; 598 | } 599 | 600 | div.sphinxsidebar a { 601 | color: #AAA; 602 | } 603 | 604 | div.sphinxsidebar p.logo { 605 | display: none; 606 | } 607 | 608 | div.document { 609 | width: 100%; 610 | margin: 0; 611 | } 612 | 613 | div.footer { 614 | display: none; 615 | } 616 | 617 | div.bodywrapper { 618 | margin: 0; 619 | } 620 | 621 | div.body { 622 | min-height: 0; 623 | padding: 0; 624 | } 625 | 626 | .rtd_doc_footer { 627 | display: none; 628 | } 629 | 630 | .document { 631 | width: auto; 632 | } 633 | 634 | .footer { 635 | width: auto; 636 | } 637 | 638 | .footer { 639 | width: auto; 640 | } 641 | 642 | .github { 643 | display: none; 644 | } 645 | } 646 | @media screen and (min-width: 876px) { 647 | div.sphinxsidebar { 648 | position: fixed; 649 | margin-left: 0; 650 | } 651 | } 652 | 653 | 654 | /* misc. */ 655 | 656 | .revsys-inline { 657 | display: none!important; 658 | } 659 | 660 | /* Hide ugly table cell borders in ..bibliography:: directive output */ 661 | table.docutils.citation, table.docutils.citation td, table.docutils.citation th { 662 | border: none; 663 | /* Below needed in some edge cases; if not applied, bottom shadows appear */ 664 | -moz-box-shadow: none; 665 | -webkit-box-shadow: none; 666 | box-shadow: none; 667 | } 668 | 669 | 670 | /* relbar */ 671 | 672 | .related { 673 | line-height: 30px; 674 | width: 100%; 675 | font-size: 0.9rem; 676 | } 677 | 678 | .related.top { 679 | border-bottom: 1px solid #EEE; 680 | margin-bottom: 20px; 681 | } 682 | 683 | .related.bottom { 684 | border-top: 1px solid #EEE; 685 | } 686 | 687 | .related ul { 688 | padding: 0; 689 | margin: 0; 690 | list-style: none; 691 | } 692 | 693 | .related li { 694 | display: inline; 695 | } 696 | 697 | nav#rellinks { 698 | float: right; 699 | } 700 | 701 | nav#rellinks li+li:before { 702 | content: "|"; 703 | } 704 | 705 | nav#breadcrumbs li+li:before { 706 | content: "\00BB"; 707 | } 708 | 709 | /* Hide certain items when printing */ 710 | @media print { 711 | div.related { 712 | display: none; 713 | } 714 | } -------------------------------------------------------------------------------- /docs/build/html/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | div.section::after { 19 | display: block; 20 | content: ''; 21 | clear: left; 22 | } 23 | 24 | /* -- relbar ---------------------------------------------------------------- */ 25 | 26 | div.related { 27 | width: 100%; 28 | font-size: 90%; 29 | } 30 | 31 | div.related h3 { 32 | display: none; 33 | } 34 | 35 | div.related ul { 36 | margin: 0; 37 | padding: 0 0 0 10px; 38 | list-style: none; 39 | } 40 | 41 | div.related li { 42 | display: inline; 43 | } 44 | 45 | div.related li.right { 46 | float: right; 47 | margin-right: 5px; 48 | } 49 | 50 | /* -- sidebar --------------------------------------------------------------- */ 51 | 52 | div.sphinxsidebarwrapper { 53 | padding: 10px 5px 0 10px; 54 | } 55 | 56 | div.sphinxsidebar { 57 | float: left; 58 | width: 230px; 59 | margin-left: -100%; 60 | font-size: 90%; 61 | word-wrap: break-word; 62 | overflow-wrap : break-word; 63 | } 64 | 65 | div.sphinxsidebar ul { 66 | list-style: none; 67 | } 68 | 69 | div.sphinxsidebar ul ul, 70 | div.sphinxsidebar ul.want-points { 71 | margin-left: 20px; 72 | list-style: square; 73 | } 74 | 75 | div.sphinxsidebar ul ul { 76 | margin-top: 0; 77 | margin-bottom: 0; 78 | } 79 | 80 | div.sphinxsidebar form { 81 | margin-top: 10px; 82 | } 83 | 84 | div.sphinxsidebar input { 85 | border: 1px solid #98dbcc; 86 | font-family: sans-serif; 87 | font-size: 1em; 88 | } 89 | 90 | div.sphinxsidebar #searchbox form.search { 91 | overflow: hidden; 92 | } 93 | 94 | div.sphinxsidebar #searchbox input[type="text"] { 95 | float: left; 96 | width: 80%; 97 | padding: 0.25em; 98 | box-sizing: border-box; 99 | } 100 | 101 | div.sphinxsidebar #searchbox input[type="submit"] { 102 | float: left; 103 | width: 20%; 104 | border-left: none; 105 | padding: 0.25em; 106 | box-sizing: border-box; 107 | } 108 | 109 | 110 | img { 111 | border: 0; 112 | max-width: 100%; 113 | } 114 | 115 | /* -- search page ----------------------------------------------------------- */ 116 | 117 | ul.search { 118 | margin: 10px 0 0 20px; 119 | padding: 0; 120 | } 121 | 122 | ul.search li { 123 | padding: 5px 0 5px 20px; 124 | background-image: url(file.png); 125 | background-repeat: no-repeat; 126 | background-position: 0 7px; 127 | } 128 | 129 | ul.search li a { 130 | font-weight: bold; 131 | } 132 | 133 | ul.search li p.context { 134 | color: #888; 135 | margin: 2px 0 0 30px; 136 | text-align: left; 137 | } 138 | 139 | ul.keywordmatches li.goodmatch a { 140 | font-weight: bold; 141 | } 142 | 143 | /* -- index page ------------------------------------------------------------ */ 144 | 145 | table.contentstable { 146 | width: 90%; 147 | margin-left: auto; 148 | margin-right: auto; 149 | } 150 | 151 | table.contentstable p.biglink { 152 | line-height: 150%; 153 | } 154 | 155 | a.biglink { 156 | font-size: 1.3em; 157 | } 158 | 159 | span.linkdescr { 160 | font-style: italic; 161 | padding-top: 5px; 162 | font-size: 90%; 163 | } 164 | 165 | /* -- general index --------------------------------------------------------- */ 166 | 167 | table.indextable { 168 | width: 100%; 169 | } 170 | 171 | table.indextable td { 172 | text-align: left; 173 | vertical-align: top; 174 | } 175 | 176 | table.indextable ul { 177 | margin-top: 0; 178 | margin-bottom: 0; 179 | list-style-type: none; 180 | } 181 | 182 | table.indextable > tbody > tr > td > ul { 183 | padding-left: 0em; 184 | } 185 | 186 | table.indextable tr.pcap { 187 | height: 10px; 188 | } 189 | 190 | table.indextable tr.cap { 191 | margin-top: 10px; 192 | background-color: #f2f2f2; 193 | } 194 | 195 | img.toggler { 196 | margin-right: 3px; 197 | margin-top: 3px; 198 | cursor: pointer; 199 | } 200 | 201 | div.modindex-jumpbox { 202 | border-top: 1px solid #ddd; 203 | border-bottom: 1px solid #ddd; 204 | margin: 1em 0 1em 0; 205 | padding: 0.4em; 206 | } 207 | 208 | div.genindex-jumpbox { 209 | border-top: 1px solid #ddd; 210 | border-bottom: 1px solid #ddd; 211 | margin: 1em 0 1em 0; 212 | padding: 0.4em; 213 | } 214 | 215 | /* -- domain module index --------------------------------------------------- */ 216 | 217 | table.modindextable td { 218 | padding: 2px; 219 | border-collapse: collapse; 220 | } 221 | 222 | /* -- general body styles --------------------------------------------------- */ 223 | 224 | div.body { 225 | min-width: inherit; 226 | max-width: 800px; 227 | } 228 | 229 | div.body p, div.body dd, div.body li, div.body blockquote { 230 | -moz-hyphens: auto; 231 | -ms-hyphens: auto; 232 | -webkit-hyphens: auto; 233 | hyphens: auto; 234 | } 235 | 236 | a.headerlink { 237 | visibility: hidden; 238 | } 239 | 240 | a:visited { 241 | color: #551A8B; 242 | } 243 | 244 | h1:hover > a.headerlink, 245 | h2:hover > a.headerlink, 246 | h3:hover > a.headerlink, 247 | h4:hover > a.headerlink, 248 | h5:hover > a.headerlink, 249 | h6:hover > a.headerlink, 250 | dt:hover > a.headerlink, 251 | caption:hover > a.headerlink, 252 | p.caption:hover > a.headerlink, 253 | div.code-block-caption:hover > a.headerlink { 254 | visibility: visible; 255 | } 256 | 257 | div.body p.caption { 258 | text-align: inherit; 259 | } 260 | 261 | div.body td { 262 | text-align: left; 263 | } 264 | 265 | .first { 266 | margin-top: 0 !important; 267 | } 268 | 269 | p.rubric { 270 | margin-top: 30px; 271 | font-weight: bold; 272 | } 273 | 274 | img.align-left, figure.align-left, .figure.align-left, object.align-left { 275 | clear: left; 276 | float: left; 277 | margin-right: 1em; 278 | } 279 | 280 | img.align-right, figure.align-right, .figure.align-right, object.align-right { 281 | clear: right; 282 | float: right; 283 | margin-left: 1em; 284 | } 285 | 286 | img.align-center, figure.align-center, .figure.align-center, object.align-center { 287 | display: block; 288 | margin-left: auto; 289 | margin-right: auto; 290 | } 291 | 292 | img.align-default, figure.align-default, .figure.align-default { 293 | display: block; 294 | margin-left: auto; 295 | margin-right: auto; 296 | } 297 | 298 | .align-left { 299 | text-align: left; 300 | } 301 | 302 | .align-center { 303 | text-align: center; 304 | } 305 | 306 | .align-default { 307 | text-align: center; 308 | } 309 | 310 | .align-right { 311 | text-align: right; 312 | } 313 | 314 | /* -- sidebars -------------------------------------------------------------- */ 315 | 316 | div.sidebar, 317 | aside.sidebar { 318 | margin: 0 0 0.5em 1em; 319 | border: 1px solid #ddb; 320 | padding: 7px; 321 | background-color: #ffe; 322 | width: 40%; 323 | float: right; 324 | clear: right; 325 | overflow-x: auto; 326 | } 327 | 328 | p.sidebar-title { 329 | font-weight: bold; 330 | } 331 | 332 | nav.contents, 333 | aside.topic, 334 | div.admonition, div.topic, blockquote { 335 | clear: left; 336 | } 337 | 338 | /* -- topics ---------------------------------------------------------------- */ 339 | 340 | nav.contents, 341 | aside.topic, 342 | div.topic { 343 | border: 1px solid #ccc; 344 | padding: 7px; 345 | margin: 10px 0 10px 0; 346 | } 347 | 348 | p.topic-title { 349 | font-size: 1.1em; 350 | font-weight: bold; 351 | margin-top: 10px; 352 | } 353 | 354 | /* -- admonitions ----------------------------------------------------------- */ 355 | 356 | div.admonition { 357 | margin-top: 10px; 358 | margin-bottom: 10px; 359 | padding: 7px; 360 | } 361 | 362 | div.admonition dt { 363 | font-weight: bold; 364 | } 365 | 366 | p.admonition-title { 367 | margin: 0px 10px 5px 0px; 368 | font-weight: bold; 369 | } 370 | 371 | div.body p.centered { 372 | text-align: center; 373 | margin-top: 25px; 374 | } 375 | 376 | /* -- content of sidebars/topics/admonitions -------------------------------- */ 377 | 378 | div.sidebar > :last-child, 379 | aside.sidebar > :last-child, 380 | nav.contents > :last-child, 381 | aside.topic > :last-child, 382 | div.topic > :last-child, 383 | div.admonition > :last-child { 384 | margin-bottom: 0; 385 | } 386 | 387 | div.sidebar::after, 388 | aside.sidebar::after, 389 | nav.contents::after, 390 | aside.topic::after, 391 | div.topic::after, 392 | div.admonition::after, 393 | blockquote::after { 394 | display: block; 395 | content: ''; 396 | clear: both; 397 | } 398 | 399 | /* -- tables ---------------------------------------------------------------- */ 400 | 401 | table.docutils { 402 | margin-top: 10px; 403 | margin-bottom: 10px; 404 | border: 0; 405 | border-collapse: collapse; 406 | } 407 | 408 | table.align-center { 409 | margin-left: auto; 410 | margin-right: auto; 411 | } 412 | 413 | table.align-default { 414 | margin-left: auto; 415 | margin-right: auto; 416 | } 417 | 418 | table caption span.caption-number { 419 | font-style: italic; 420 | } 421 | 422 | table caption span.caption-text { 423 | } 424 | 425 | table.docutils td, table.docutils th { 426 | padding: 1px 8px 1px 5px; 427 | border-top: 0; 428 | border-left: 0; 429 | border-right: 0; 430 | border-bottom: 1px solid #aaa; 431 | } 432 | 433 | th { 434 | text-align: left; 435 | padding-right: 5px; 436 | } 437 | 438 | table.citation { 439 | border-left: solid 1px gray; 440 | margin-left: 1px; 441 | } 442 | 443 | table.citation td { 444 | border-bottom: none; 445 | } 446 | 447 | th > :first-child, 448 | td > :first-child { 449 | margin-top: 0px; 450 | } 451 | 452 | th > :last-child, 453 | td > :last-child { 454 | margin-bottom: 0px; 455 | } 456 | 457 | /* -- figures --------------------------------------------------------------- */ 458 | 459 | div.figure, figure { 460 | margin: 0.5em; 461 | padding: 0.5em; 462 | } 463 | 464 | div.figure p.caption, figcaption { 465 | padding: 0.3em; 466 | } 467 | 468 | div.figure p.caption span.caption-number, 469 | figcaption span.caption-number { 470 | font-style: italic; 471 | } 472 | 473 | div.figure p.caption span.caption-text, 474 | figcaption span.caption-text { 475 | } 476 | 477 | /* -- field list styles ----------------------------------------------------- */ 478 | 479 | table.field-list td, table.field-list th { 480 | border: 0 !important; 481 | } 482 | 483 | .field-list ul { 484 | margin: 0; 485 | padding-left: 1em; 486 | } 487 | 488 | .field-list p { 489 | margin: 0; 490 | } 491 | 492 | .field-name { 493 | -moz-hyphens: manual; 494 | -ms-hyphens: manual; 495 | -webkit-hyphens: manual; 496 | hyphens: manual; 497 | } 498 | 499 | /* -- hlist styles ---------------------------------------------------------- */ 500 | 501 | table.hlist { 502 | margin: 1em 0; 503 | } 504 | 505 | table.hlist td { 506 | vertical-align: top; 507 | } 508 | 509 | /* -- object description styles --------------------------------------------- */ 510 | 511 | .sig { 512 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 513 | } 514 | 515 | .sig-name, code.descname { 516 | background-color: transparent; 517 | font-weight: bold; 518 | } 519 | 520 | .sig-name { 521 | font-size: 1.1em; 522 | } 523 | 524 | code.descname { 525 | font-size: 1.2em; 526 | } 527 | 528 | .sig-prename, code.descclassname { 529 | background-color: transparent; 530 | } 531 | 532 | .optional { 533 | font-size: 1.3em; 534 | } 535 | 536 | .sig-paren { 537 | font-size: larger; 538 | } 539 | 540 | .sig-param.n { 541 | font-style: italic; 542 | } 543 | 544 | /* C++ specific styling */ 545 | 546 | .sig-inline.c-texpr, 547 | .sig-inline.cpp-texpr { 548 | font-family: unset; 549 | } 550 | 551 | .sig.c .k, .sig.c .kt, 552 | .sig.cpp .k, .sig.cpp .kt { 553 | color: #0033B3; 554 | } 555 | 556 | .sig.c .m, 557 | .sig.cpp .m { 558 | color: #1750EB; 559 | } 560 | 561 | .sig.c .s, .sig.c .sc, 562 | .sig.cpp .s, .sig.cpp .sc { 563 | color: #067D17; 564 | } 565 | 566 | 567 | /* -- other body styles ----------------------------------------------------- */ 568 | 569 | ol.arabic { 570 | list-style: decimal; 571 | } 572 | 573 | ol.loweralpha { 574 | list-style: lower-alpha; 575 | } 576 | 577 | ol.upperalpha { 578 | list-style: upper-alpha; 579 | } 580 | 581 | ol.lowerroman { 582 | list-style: lower-roman; 583 | } 584 | 585 | ol.upperroman { 586 | list-style: upper-roman; 587 | } 588 | 589 | :not(li) > ol > li:first-child > :first-child, 590 | :not(li) > ul > li:first-child > :first-child { 591 | margin-top: 0px; 592 | } 593 | 594 | :not(li) > ol > li:last-child > :last-child, 595 | :not(li) > ul > li:last-child > :last-child { 596 | margin-bottom: 0px; 597 | } 598 | 599 | ol.simple ol p, 600 | ol.simple ul p, 601 | ul.simple ol p, 602 | ul.simple ul p { 603 | margin-top: 0; 604 | } 605 | 606 | ol.simple > li:not(:first-child) > p, 607 | ul.simple > li:not(:first-child) > p { 608 | margin-top: 0; 609 | } 610 | 611 | ol.simple p, 612 | ul.simple p { 613 | margin-bottom: 0; 614 | } 615 | 616 | aside.footnote > span, 617 | div.citation > span { 618 | float: left; 619 | } 620 | aside.footnote > span:last-of-type, 621 | div.citation > span:last-of-type { 622 | padding-right: 0.5em; 623 | } 624 | aside.footnote > p { 625 | margin-left: 2em; 626 | } 627 | div.citation > p { 628 | margin-left: 4em; 629 | } 630 | aside.footnote > p:last-of-type, 631 | div.citation > p:last-of-type { 632 | margin-bottom: 0em; 633 | } 634 | aside.footnote > p:last-of-type:after, 635 | div.citation > p:last-of-type:after { 636 | content: ""; 637 | clear: both; 638 | } 639 | 640 | dl.field-list { 641 | display: grid; 642 | grid-template-columns: fit-content(30%) auto; 643 | } 644 | 645 | dl.field-list > dt { 646 | font-weight: bold; 647 | word-break: break-word; 648 | padding-left: 0.5em; 649 | padding-right: 5px; 650 | } 651 | 652 | dl.field-list > dd { 653 | padding-left: 0.5em; 654 | margin-top: 0em; 655 | margin-left: 0em; 656 | margin-bottom: 0em; 657 | } 658 | 659 | dl { 660 | margin-bottom: 15px; 661 | } 662 | 663 | dd > :first-child { 664 | margin-top: 0px; 665 | } 666 | 667 | dd ul, dd table { 668 | margin-bottom: 10px; 669 | } 670 | 671 | dd { 672 | margin-top: 3px; 673 | margin-bottom: 10px; 674 | margin-left: 30px; 675 | } 676 | 677 | .sig dd { 678 | margin-top: 0px; 679 | margin-bottom: 0px; 680 | } 681 | 682 | .sig dl { 683 | margin-top: 0px; 684 | margin-bottom: 0px; 685 | } 686 | 687 | dl > dd:last-child, 688 | dl > dd:last-child > :last-child { 689 | margin-bottom: 0; 690 | } 691 | 692 | dt:target, span.highlighted { 693 | background-color: #fbe54e; 694 | } 695 | 696 | rect.highlighted { 697 | fill: #fbe54e; 698 | } 699 | 700 | dl.glossary dt { 701 | font-weight: bold; 702 | font-size: 1.1em; 703 | } 704 | 705 | .versionmodified { 706 | font-style: italic; 707 | } 708 | 709 | .system-message { 710 | background-color: #fda; 711 | padding: 5px; 712 | border: 3px solid red; 713 | } 714 | 715 | .footnote:target { 716 | background-color: #ffa; 717 | } 718 | 719 | .line-block { 720 | display: block; 721 | margin-top: 1em; 722 | margin-bottom: 1em; 723 | } 724 | 725 | .line-block .line-block { 726 | margin-top: 0; 727 | margin-bottom: 0; 728 | margin-left: 1.5em; 729 | } 730 | 731 | .guilabel, .menuselection { 732 | font-family: sans-serif; 733 | } 734 | 735 | .accelerator { 736 | text-decoration: underline; 737 | } 738 | 739 | .classifier { 740 | font-style: oblique; 741 | } 742 | 743 | .classifier:before { 744 | font-style: normal; 745 | margin: 0 0.5em; 746 | content: ":"; 747 | display: inline-block; 748 | } 749 | 750 | abbr, acronym { 751 | border-bottom: dotted 1px; 752 | cursor: help; 753 | } 754 | 755 | .translated { 756 | background-color: rgba(207, 255, 207, 0.2) 757 | } 758 | 759 | .untranslated { 760 | background-color: rgba(255, 207, 207, 0.2) 761 | } 762 | 763 | /* -- code displays --------------------------------------------------------- */ 764 | 765 | pre { 766 | overflow: auto; 767 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 768 | } 769 | 770 | pre, div[class*="highlight-"] { 771 | clear: both; 772 | } 773 | 774 | span.pre { 775 | -moz-hyphens: none; 776 | -ms-hyphens: none; 777 | -webkit-hyphens: none; 778 | hyphens: none; 779 | white-space: nowrap; 780 | } 781 | 782 | div[class*="highlight-"] { 783 | margin: 1em 0; 784 | } 785 | 786 | td.linenos pre { 787 | border: 0; 788 | background-color: transparent; 789 | color: #aaa; 790 | } 791 | 792 | table.highlighttable { 793 | display: block; 794 | } 795 | 796 | table.highlighttable tbody { 797 | display: block; 798 | } 799 | 800 | table.highlighttable tr { 801 | display: flex; 802 | } 803 | 804 | table.highlighttable td { 805 | margin: 0; 806 | padding: 0; 807 | } 808 | 809 | table.highlighttable td.linenos { 810 | padding-right: 0.5em; 811 | } 812 | 813 | table.highlighttable td.code { 814 | flex: 1; 815 | overflow: hidden; 816 | } 817 | 818 | .highlight .hll { 819 | display: block; 820 | } 821 | 822 | div.highlight pre, 823 | table.highlighttable pre { 824 | margin: 0; 825 | } 826 | 827 | div.code-block-caption + div { 828 | margin-top: 0; 829 | } 830 | 831 | div.code-block-caption { 832 | margin-top: 1em; 833 | padding: 2px 5px; 834 | font-size: small; 835 | } 836 | 837 | div.code-block-caption code { 838 | background-color: transparent; 839 | } 840 | 841 | table.highlighttable td.linenos, 842 | span.linenos, 843 | div.highlight span.gp { /* gp: Generic.Prompt */ 844 | user-select: none; 845 | -webkit-user-select: text; /* Safari fallback only */ 846 | -webkit-user-select: none; /* Chrome/Safari */ 847 | -moz-user-select: none; /* Firefox */ 848 | -ms-user-select: none; /* IE10+ */ 849 | } 850 | 851 | div.code-block-caption span.caption-number { 852 | padding: 0.1em 0.3em; 853 | font-style: italic; 854 | } 855 | 856 | div.code-block-caption span.caption-text { 857 | } 858 | 859 | div.literal-block-wrapper { 860 | margin: 1em 0; 861 | } 862 | 863 | code.xref, a code { 864 | background-color: transparent; 865 | font-weight: bold; 866 | } 867 | 868 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 869 | background-color: transparent; 870 | } 871 | 872 | .viewcode-link { 873 | float: right; 874 | } 875 | 876 | .viewcode-back { 877 | float: right; 878 | font-family: sans-serif; 879 | } 880 | 881 | div.viewcode-block:target { 882 | margin: -1px -10px; 883 | padding: 0 10px; 884 | } 885 | 886 | /* -- math display ---------------------------------------------------------- */ 887 | 888 | img.math { 889 | vertical-align: middle; 890 | } 891 | 892 | div.body div.math p { 893 | text-align: center; 894 | } 895 | 896 | span.eqno { 897 | float: right; 898 | } 899 | 900 | span.eqno a.headerlink { 901 | position: absolute; 902 | z-index: 1; 903 | } 904 | 905 | div.math:hover a.headerlink { 906 | visibility: visible; 907 | } 908 | 909 | /* -- printout stylesheet --------------------------------------------------- */ 910 | 911 | @media print { 912 | div.document, 913 | div.documentwrapper, 914 | div.bodywrapper { 915 | margin: 0 !important; 916 | width: 100%; 917 | } 918 | 919 | div.sphinxsidebar, 920 | div.related, 921 | div.footer, 922 | #top-link { 923 | display: none; 924 | } 925 | } -------------------------------------------------------------------------------- /docs/build/html/_static/caltopo_python_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/_static/caltopo_python_logo.png -------------------------------------------------------------------------------- /docs/build/html/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 14 | "TEXTAREA", 15 | "INPUT", 16 | "SELECT", 17 | "BUTTON", 18 | ]); 19 | 20 | const _ready = (callback) => { 21 | if (document.readyState !== "loading") { 22 | callback(); 23 | } else { 24 | document.addEventListener("DOMContentLoaded", callback); 25 | } 26 | }; 27 | 28 | /** 29 | * Small JavaScript module for the documentation. 30 | */ 31 | const Documentation = { 32 | init: () => { 33 | Documentation.initDomainIndexTable(); 34 | Documentation.initOnKeyListeners(); 35 | }, 36 | 37 | /** 38 | * i18n support 39 | */ 40 | TRANSLATIONS: {}, 41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 42 | LOCALE: "unknown", 43 | 44 | // gettext and ngettext don't access this so that the functions 45 | // can safely bound to a different name (_ = Documentation.gettext) 46 | gettext: (string) => { 47 | const translated = Documentation.TRANSLATIONS[string]; 48 | switch (typeof translated) { 49 | case "undefined": 50 | return string; // no translation 51 | case "string": 52 | return translated; // translation exists 53 | default: 54 | return translated[0]; // (singular, plural) translation tuple exists 55 | } 56 | }, 57 | 58 | ngettext: (singular, plural, n) => { 59 | const translated = Documentation.TRANSLATIONS[singular]; 60 | if (typeof translated !== "undefined") 61 | return translated[Documentation.PLURAL_EXPR(n)]; 62 | return n === 1 ? singular : plural; 63 | }, 64 | 65 | addTranslations: (catalog) => { 66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 67 | Documentation.PLURAL_EXPR = new Function( 68 | "n", 69 | `return (${catalog.plural_expr})` 70 | ); 71 | Documentation.LOCALE = catalog.locale; 72 | }, 73 | 74 | /** 75 | * helper function to focus on search bar 76 | */ 77 | focusSearchBar: () => { 78 | document.querySelectorAll("input[name=q]")[0]?.focus(); 79 | }, 80 | 81 | /** 82 | * Initialise the domain index toggle buttons 83 | */ 84 | initDomainIndexTable: () => { 85 | const toggler = (el) => { 86 | const idNumber = el.id.substr(7); 87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 88 | if (el.src.substr(-9) === "minus.png") { 89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 90 | toggledRows.forEach((el) => (el.style.display = "none")); 91 | } else { 92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 93 | toggledRows.forEach((el) => (el.style.display = "")); 94 | } 95 | }; 96 | 97 | const togglerElements = document.querySelectorAll("img.toggler"); 98 | togglerElements.forEach((el) => 99 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 100 | ); 101 | togglerElements.forEach((el) => (el.style.display = "")); 102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 103 | }, 104 | 105 | initOnKeyListeners: () => { 106 | // only install a listener if it is really needed 107 | if ( 108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 110 | ) 111 | return; 112 | 113 | document.addEventListener("keydown", (event) => { 114 | // bail for input elements 115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 116 | // bail with special keys 117 | if (event.altKey || event.ctrlKey || event.metaKey) return; 118 | 119 | if (!event.shiftKey) { 120 | switch (event.key) { 121 | case "ArrowLeft": 122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 123 | 124 | const prevLink = document.querySelector('link[rel="prev"]'); 125 | if (prevLink && prevLink.href) { 126 | window.location.href = prevLink.href; 127 | event.preventDefault(); 128 | } 129 | break; 130 | case "ArrowRight": 131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 132 | 133 | const nextLink = document.querySelector('link[rel="next"]'); 134 | if (nextLink && nextLink.href) { 135 | window.location.href = nextLink.href; 136 | event.preventDefault(); 137 | } 138 | break; 139 | } 140 | } 141 | 142 | // some keyboard layouts may need Shift to get / 143 | switch (event.key) { 144 | case "/": 145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 146 | Documentation.focusSearchBar(); 147 | event.preventDefault(); 148 | } 149 | }); 150 | }, 151 | }; 152 | 153 | // quick alias for translations 154 | const _ = Documentation.gettext; 155 | 156 | _ready(Documentation.init); 157 | -------------------------------------------------------------------------------- /docs/build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | const DOCUMENTATION_OPTIONS = { 2 | VERSION: '2.0.0', 3 | LANGUAGE: 'en', 4 | COLLAPSE_INDEX: false, 5 | BUILDER: 'html', 6 | FILE_SUFFIX: '.html', 7 | LINK_SUFFIX: '.html', 8 | HAS_SOURCE: true, 9 | SOURCELINK_SUFFIX: '.txt', 10 | NAVIGATION_WITH_KEYS: false, 11 | SHOW_SEARCH_SUMMARY: true, 12 | ENABLE_SEARCH_SHORTCUTS: true, 13 | }; -------------------------------------------------------------------------------- /docs/build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/_static/file.png -------------------------------------------------------------------------------- /docs/build/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, if available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #f8f8f8; } 8 | .highlight .c { color: #8f5902; font-style: italic } /* Comment */ 9 | .highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ 10 | .highlight .g { color: #000000 } /* Generic */ 11 | .highlight .k { color: #004461; font-weight: bold } /* Keyword */ 12 | .highlight .l { color: #000000 } /* Literal */ 13 | .highlight .n { color: #000000 } /* Name */ 14 | .highlight .o { color: #582800 } /* Operator */ 15 | .highlight .x { color: #000000 } /* Other */ 16 | .highlight .p { color: #000000; font-weight: bold } /* Punctuation */ 17 | .highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ 18 | .highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ 19 | .highlight .cp { color: #8f5902 } /* Comment.Preproc */ 20 | .highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ 21 | .highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ 22 | .highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ 23 | .highlight .gd { color: #a40000 } /* Generic.Deleted */ 24 | .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ 25 | .highlight .ges { color: #000000 } /* Generic.EmphStrong */ 26 | .highlight .gr { color: #ef2929 } /* Generic.Error */ 27 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 28 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 29 | .highlight .go { color: #888888 } /* Generic.Output */ 30 | .highlight .gp { color: #745334 } /* Generic.Prompt */ 31 | .highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ 32 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 33 | .highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ 34 | .highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ 35 | .highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ 36 | .highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ 37 | .highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ 38 | .highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ 39 | .highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ 40 | .highlight .ld { color: #000000 } /* Literal.Date */ 41 | .highlight .m { color: #990000 } /* Literal.Number */ 42 | .highlight .s { color: #4e9a06 } /* Literal.String */ 43 | .highlight .na { color: #c4a000 } /* Name.Attribute */ 44 | .highlight .nb { color: #004461 } /* Name.Builtin */ 45 | .highlight .nc { color: #000000 } /* Name.Class */ 46 | .highlight .no { color: #000000 } /* Name.Constant */ 47 | .highlight .nd { color: #888888 } /* Name.Decorator */ 48 | .highlight .ni { color: #ce5c00 } /* Name.Entity */ 49 | .highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ 50 | .highlight .nf { color: #000000 } /* Name.Function */ 51 | .highlight .nl { color: #f57900 } /* Name.Label */ 52 | .highlight .nn { color: #000000 } /* Name.Namespace */ 53 | .highlight .nx { color: #000000 } /* Name.Other */ 54 | .highlight .py { color: #000000 } /* Name.Property */ 55 | .highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ 56 | .highlight .nv { color: #000000 } /* Name.Variable */ 57 | .highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ 58 | .highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ 59 | .highlight .w { color: #f8f8f8 } /* Text.Whitespace */ 60 | .highlight .mb { color: #990000 } /* Literal.Number.Bin */ 61 | .highlight .mf { color: #990000 } /* Literal.Number.Float */ 62 | .highlight .mh { color: #990000 } /* Literal.Number.Hex */ 63 | .highlight .mi { color: #990000 } /* Literal.Number.Integer */ 64 | .highlight .mo { color: #990000 } /* Literal.Number.Oct */ 65 | .highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ 66 | .highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ 67 | .highlight .sc { color: #4e9a06 } /* Literal.String.Char */ 68 | .highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ 69 | .highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ 70 | .highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ 71 | .highlight .se { color: #4e9a06 } /* Literal.String.Escape */ 72 | .highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ 73 | .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ 74 | .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ 75 | .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ 76 | .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ 77 | .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ 78 | .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ 79 | .highlight .fm { color: #000000 } /* Name.Function.Magic */ 80 | .highlight .vc { color: #000000 } /* Name.Variable.Class */ 81 | .highlight .vg { color: #000000 } /* Name.Variable.Global */ 82 | .highlight .vi { color: #000000 } /* Name.Variable.Instance */ 83 | .highlight .vm { color: #000000 } /* Name.Variable.Magic */ 84 | .highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/build/html/_static/sphinx_highlight.js: -------------------------------------------------------------------------------- 1 | /* Highlighting utilities for Sphinx HTML documentation. */ 2 | "use strict"; 3 | 4 | const SPHINX_HIGHLIGHT_ENABLED = true 5 | 6 | /** 7 | * highlight a given string on a node by wrapping it in 8 | * span elements with the given class name. 9 | */ 10 | const _highlight = (node, addItems, text, className) => { 11 | if (node.nodeType === Node.TEXT_NODE) { 12 | const val = node.nodeValue; 13 | const parent = node.parentNode; 14 | const pos = val.toLowerCase().indexOf(text); 15 | if ( 16 | pos >= 0 && 17 | !parent.classList.contains(className) && 18 | !parent.classList.contains("nohighlight") 19 | ) { 20 | let span; 21 | 22 | const closestNode = parent.closest("body, svg, foreignObject"); 23 | const isInSVG = closestNode && closestNode.matches("svg"); 24 | if (isInSVG) { 25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 26 | } else { 27 | span = document.createElement("span"); 28 | span.classList.add(className); 29 | } 30 | 31 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 32 | const rest = document.createTextNode(val.substr(pos + text.length)); 33 | parent.insertBefore( 34 | span, 35 | parent.insertBefore( 36 | rest, 37 | node.nextSibling 38 | ) 39 | ); 40 | node.nodeValue = val.substr(0, pos); 41 | /* There may be more occurrences of search term in this node. So call this 42 | * function recursively on the remaining fragment. 43 | */ 44 | _highlight(rest, addItems, text, className); 45 | 46 | if (isInSVG) { 47 | const rect = document.createElementNS( 48 | "http://www.w3.org/2000/svg", 49 | "rect" 50 | ); 51 | const bbox = parent.getBBox(); 52 | rect.x.baseVal.value = bbox.x; 53 | rect.y.baseVal.value = bbox.y; 54 | rect.width.baseVal.value = bbox.width; 55 | rect.height.baseVal.value = bbox.height; 56 | rect.setAttribute("class", className); 57 | addItems.push({ parent: parent, target: rect }); 58 | } 59 | } 60 | } else if (node.matches && !node.matches("button, select, textarea")) { 61 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 62 | } 63 | }; 64 | const _highlightText = (thisNode, text, className) => { 65 | let addItems = []; 66 | _highlight(thisNode, addItems, text, className); 67 | addItems.forEach((obj) => 68 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 69 | ); 70 | }; 71 | 72 | /** 73 | * Small JavaScript module for the documentation. 74 | */ 75 | const SphinxHighlight = { 76 | 77 | /** 78 | * highlight the search words provided in localstorage in the text 79 | */ 80 | highlightSearchWords: () => { 81 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight 82 | 83 | // get and clear terms from localstorage 84 | const url = new URL(window.location); 85 | const highlight = 86 | localStorage.getItem("sphinx_highlight_terms") 87 | || url.searchParams.get("highlight") 88 | || ""; 89 | localStorage.removeItem("sphinx_highlight_terms") 90 | url.searchParams.delete("highlight"); 91 | window.history.replaceState({}, "", url); 92 | 93 | // get individual terms from highlight string 94 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 95 | if (terms.length === 0) return; // nothing to do 96 | 97 | // There should never be more than one element matching "div.body" 98 | const divBody = document.querySelectorAll("div.body"); 99 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 100 | window.setTimeout(() => { 101 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 102 | }, 10); 103 | 104 | const searchBox = document.getElementById("searchbox"); 105 | if (searchBox === null) return; 106 | searchBox.appendChild( 107 | document 108 | .createRange() 109 | .createContextualFragment( 110 | '" 114 | ) 115 | ); 116 | }, 117 | 118 | /** 119 | * helper function to hide the search marks again 120 | */ 121 | hideSearchWords: () => { 122 | document 123 | .querySelectorAll("#searchbox .highlight-link") 124 | .forEach((el) => el.remove()); 125 | document 126 | .querySelectorAll("span.highlighted") 127 | .forEach((el) => el.classList.remove("highlighted")); 128 | localStorage.removeItem("sphinx_highlight_terms") 129 | }, 130 | 131 | initEscapeListener: () => { 132 | // only install a listener if it is really needed 133 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 134 | 135 | document.addEventListener("keydown", (event) => { 136 | // bail for input elements 137 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 138 | // bail with special keys 139 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; 140 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { 141 | SphinxHighlight.hideSearchWords(); 142 | event.preventDefault(); 143 | } 144 | }); 145 | }, 146 | }; 147 | 148 | _ready(() => { 149 | /* Do not call highlightSearchWords() when we are on the search page. 150 | * It will highlight words from the *previous* search query. 151 | */ 152 | if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); 153 | SphinxHighlight.initEscapeListener(); 154 | }); 155 | -------------------------------------------------------------------------------- /docs/build/html/credentials.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Credentials — sartopo_python 2.0.0 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 51 |
52 |
53 | 54 | 55 |
56 | 57 |
58 |

Credentials

59 |

To activate online requests, you need to determine these values:

60 |
    61 |
  • account ID (6 characters)

  • 62 |
  • credential ID (12 characters)

  • 63 |
  • public key (44 characters)

  • 64 |
65 |

You can place these values in the configuration file, or you can specify them as arguments during session initialization. 66 | See examples at the bottom of this page.

67 |

This section only refers to your CalTopo account credentials - it does not refer to your external account provider credentials 68 | (Google, Yahoo, MSN, Apple, etc.). This module does not need credentials for your external account provider.

69 |

Your CalTopo account may have multiple sets of credentials. These show up in the ‘Credentials’ section at the bottom 70 | of the ‘Your Account’ dialog.

71 |

To open the ‘Your Account’ dialog, sign in to caltopo.com then click your login ID name, to the right of 72 | ‘Your Data’ near the top right of the web interface. Don’t worry if no credentials are listed yet.

73 |

Each credential has a ‘credential ID’ (the 12-character code shown in the Credentials table), 74 | and a ‘public key’, which takes a bit more work to find.

75 |

Currently, the public key is most easily determined during the process of creating a new credential.

76 |

To create a new credential and to determine its credential ID and public key, follow these steps (based on the README at https://github.com/elliottshane/sme-sartopo-mapsrv):

77 |
    78 |
  1. Open a web page to caltopo.com. Make sure you are signed in to your account: 79 | you should see your user name or login name at the top right, to the right of ‘Your Data’.

  2. 80 |
  3. In a separate browser tab, go to https://caltopo.com/app/activate/offline?redirect=localhost. 81 | This should show a web page similar to the one used during CalTopo Desktop activation from the CalTopo Desktop Installation instructions. Don’t click Sync Account yet.

  4. 82 |
  5. Open the developer console of your browser and start monitoring network traffic. 83 | For Chrome, use F12 to open Chrome DevTools; network traffic logging should be on when you open DevTools, 84 | as indicated by a red square-in-circle near the top left, which would stop monitoring network traffic 85 | when clicked.

  6. 86 |
  7. Type ‘sartopo_python’ or a similar name for ‘Your device will be synced as’. The exact name is not important, 87 | but can help you keep track of credentials in case you have several. Afterwards, the name you enter here will 88 | show up in the Credentials section of the Your Account dialog as above.

  8. 89 |
  9. Check the checkbox and click Sync Account. (This should load an error page, which is OK.)

  10. 90 |
  11. In the network traffic monitor, you will see many requests. After a few seconds, you can stop or pause 91 | network traffic monitoring to make sure the important entry does not get scrolled away as more new traffic happens.

  12. 92 |
  13. In the first few requests, at the top of the list, you should see a request similar to:

    93 |
    finish-activate?code=........&name=......
     94 | 
    95 |
    96 |

    Write down or copy the 8-character value after ‘code=’ from that request. This is not the value to put in the 97 | configuration file; you will use it in the next step.

    98 |
  14. 99 |
  15. In a new browser tab, go to:

    100 |
    caltopo.com/api/v1/activate?code=<code>
    101 | 
    102 |
    103 |
  16. 104 |
105 |
106 |

replacing <code> with the 8-character code from the previous step.

107 |
108 |
    109 |
  1. This should load a page that looks like the following (possibly all compressed into one line):

  2. 110 |
111 |
{
112 |   "code": "XXXXXXXXXXX",
113 |   "account": {
114 |     "id": "ABC123",
115 |     "type": "Feature",
116 |     "properties": {
117 |       "subscriptionExpires": 1554760038,
118 |       "subscriptionType": "pro-1",
119 |       "subscriptionRenew": true,
120 |       "subscriptionStatus": "active",
121 |       "title": "......@example",
122 |       "class": "UserAccount",
123 |       "updated": 1554760038,
124 |       "email": "......@example.com"
125 |     }
126 |   },
127 |   "key": "xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX="
128 | }
129 | 
130 |
131 |
    132 |
  1. Enter the 12-character ‘code’ value as ‘id’ in the configuration file. Enter the 44-character value of ‘key’ 133 | as ‘key’ in the configuration file. Enter the 6-character ‘id’ value as ‘accountId’ in the configuration file:

    134 |
    # sartopo_python config file
    135 | # This file contains credentials used to send API map requests
    136 | #  to sartopo.com.  Protect and do not distribute these credentials.
    137 | [joe@example.com]
    138 | id=XXXXXXXXXXXX
    139 | key=xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=
    140 | accountId=ABC123
    141 | 
    142 |
    143 |
  2. 144 |
145 |
146 |

Alternately, any of these can be specified as arguments when initializing the session, which will override values 147 | from the configuration file (if any):

148 |
149 |
# to use the config file: specify filename and account name
150 | sts=SartopoSession('sartopo.com',
151 |         configpath='../../sts.ini',
152 |         account='joe@gmail.com')
153 | 
154 | # to use arguments instead of the config file:
155 | sts=SartopoSession('sartopo.com',
156 |         id='XXXXXXXXXXXX',
157 |         key='xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=',
158 |         accountId='ABC123')
159 | 
160 | # to use the config file, but use arguments to override values from the config file:
161 | sts=SartopoSession('sartopo.com',
162 |         configpath='../../sts.ini',
163 |         account='joe@gmail.com',
164 |         id='XXXXXXXXXXXX',
165 |         key='xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=',
166 |         accountId='ABC123')
167 | 
168 |
169 |
170 | 171 | 172 |
173 | 174 |
175 |
176 |
177 |
178 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /docs/build/html/fixedChoices.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Fixed choices — sartopo_python 2.0.0 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 51 |
52 |
53 | 54 | 55 |
56 | 57 |
58 |

Fixed choices

59 |

For some property and argument values that are specified as strings, only certain values will be processed correctly.

60 |

These fixed lists of choices are determined by caltopo.com - not by the authors of this module. The lists below are (hopefully) accurate at the time of writing, but, could change at any time.

61 |

The best option is to inspect outgoing network traffic while creating or editing a feature from the web interface with the selection you want.

62 |
    63 |
  • 64 |
    Marker symbol name

    There are well over a hundred available marker symbols. Only a few common ones are listed here. Use the technique above to find the one you want.

    65 |

    point, c:ring, c:target1, cp, heatsource, clue

    66 |
    67 |
    68 |
  • 69 |
  • 70 |
    Line pattern

    These formatted strings are too detailed to list here. Use the technique above to find the one you want.

    71 |
    72 |
    73 |
  • 74 |
  • 75 |
    Feature class
    76 |
    Shape, Marker, AppTrack, LiveTrack, Folder, MapMediaObject, OperationalPeriod, Assignment, Clue, Resource, SmsLocationRequest
      77 |
    • NOTE: Polygons and Lines are both part of the ‘Shape’ feature class, but are differentiated by the ‘Geometry type’ of ‘Polygon’ vs. ‘LineString’.

    • 78 |
    79 |
    80 |
    81 |
    82 |
    83 |
  • 84 |
  • 85 |
    Assignment priority

    HIGH, MEDIUM, LOW

    86 |
    87 |
    88 |
  • 89 |
  • 90 |
    Assignment POD (Responsive, Unresponsive, Clue)

    HIGH, MEDIUM, LOW

    91 |
    92 |
    93 |
  • 94 |
  • 95 |
    Assignment status

    DRAFT, PREPARED, INPROGRESS, COMPLETED

    96 |
    97 |
    98 |
  • 99 |
  • Assignment resource type

    100 |
    101 |

    GROUND, GROUND_1, GROUND_2, GROUND_3, DOG, DOG_TRAIL, DOG_AREA, DOG_HRD, OHV, BIKE, WATER, MOUNTED, AIR

    102 |
    103 |
      104 |
    • NOTE: Underscores are used in the actual property values, but the web interface will display hyphens instead of underscores.

    • 105 |
    106 |
    107 |
    108 |
  • 109 |
110 |
111 | 112 | 113 |
114 | 115 |
116 |
117 |
118 |
119 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /docs/build/html/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Index — sartopo_python 2.0.0 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 50 |
51 |
52 | 53 | 54 |
55 | 56 | 57 |

Index

58 | 59 |
60 | _ 61 | | A 62 | | C 63 | | D 64 | | E 65 | | F 66 | | G 67 | | M 68 | | O 69 | | S 70 | 71 |
72 |

_

73 | 74 | 98 | 124 |
125 | 126 |

A

127 | 128 | 138 | 148 |
149 | 150 |

C

151 | 152 | 156 | 160 |
161 | 162 |

D

163 | 164 | 170 | 176 |
177 | 178 |

E

179 | 180 | 184 | 190 |
191 | 192 |

F

193 | 194 | 198 |
199 | 200 |

G

201 | 202 | 212 | 222 |
223 | 224 |

M

225 | 226 | 230 |
231 | 232 |

O

233 | 234 | 238 |
239 | 240 |

S

241 | 242 | 246 |
247 | 248 | 249 | 250 |
251 | 252 |
253 |
254 |
255 |
256 | 264 | 265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /docs/build/html/migration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Migration — sartopo_python 2.0.0 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 62 |
63 |
64 | 65 | 66 |
67 | 68 |
69 |

Migration

70 |

There are two categories of migration that you might be concerned with:

71 |
72 |

1. sartopo_python is changing names to caltopo_python

73 |

This module began as ‘sartopo_python’. There are several reasons for changing the module name, including:

74 |
    75 |
  • most of the features in this module are not SAR-specific

  • 76 |
  • sartopo.com is now effectively the same as caltopo.com; the only difference is the ‘SAR’ vs. ‘Recreation’ mode setting for each map

  • 77 |
  • caltopo.com has much broader name recognition than sartopo.com outside of the SAR community

  • 78 |
  • CalTopo is the name of the app; there is no ‘SARTopo app’

  • 79 |
  • the name of the downloadble server is CalTopo Desktop (formerly SARTopo Offline or CalTopo Offline); there is no ‘SARTopo Desktop’

  • 80 |
81 |

caltopo_python 1.0.x will be identical to sartopo_python 2.0.x.

82 |

sartopo_python will not receive any updates after 2.0.x. That is, there will be no sartopo_python 2.1.0. 83 | Patches / bug fixes to 1.0 / 2.0 will be applied to both packages, but, 84 | minor and major version updates will only be applied to caltopo_python.

85 |

We suggest that you change to caltopo_python as soon as possible. If you are just getting started with sartopo_python, we suggest that you use caltopo_python instead.

86 |

How do I migrate from sartopo_python to caltopo_python?

87 |
    88 |
  1. pip install caltopo_python

  2. 89 |
  3. import caltopo_python instead of import sartopo_python

  4. 90 |
  5. create an instance of CaltopoSession instead of SartopoSession

  6. 91 |
92 |

There is no change to class method names or signatures. The class name is the only difference.

93 |
94 |
95 |

2. migrating to sartopo_python 2.0.0 from an earlier version

96 |

(or, migrating to caltopo_python 1.0.0 from an earlier version of sartopo_python)

97 |

Some class method names and signatures have changed from earlier versions.

98 |

Several method names now start with an underscore, to indicate that they are not likely to be needed directly 99 | in your downstream code. These ‘internal’ data management and helper methods are normally only called internally 100 | by other class methods.

101 |

They can still be called from your downstream code if there is a specific need, as long as you are fully aware 102 | of their impacts on internal class data - especially the local cache and the threaded queueing operations.

103 |

These previously-non-underscored methods from the latest PyPi version (1.1.2) are now ‘internal’ (e.g. .setupSession is now ._setupSession):

104 |
    105 |
  • ._setupSession, ._sendRequest

  • 106 |
107 |

In addition, these previously-non-underscored methods from more recent versions of source code are now ‘internal’:

108 |
    109 |
  • ._sendUserdata, ._doSync, ._refresh, ._start, ._stop, ._pause, ._resume, ._buffer2, ._intersection2, ._caseMatch, ._twoify, ._fourify, ._removeDuplicatePoints, ._removeSpurs, ._getUsedSuffixList, ._getNextAvailableSuffix, ._getToken

  • 110 |
111 |

In general, the changes from the latest PyPi version (1.1.2) are extensive enough that documenting 112 | the differences would not be any more helpful than looking in the Class Reference.

113 |

Changes from more recent versions of source code mainly involve method signatures (argument names, types, and sequences).

114 |

In either case, migrating your code from previous versions may be a fair amount of work, but the new feature set and 115 | forward compatibility should be a good payoff. Use the Examples section of the main page, 116 | and the Class Reference, as your guides.

117 |
118 |
119 | 120 | 121 |
122 | 123 |
124 |
125 |
126 |
127 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/build/html/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | sartopo_python — sartopo_python 2.0.0 documentation 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 49 |
50 |
51 | 52 | 53 | 145 | 146 |
147 |
148 |
149 |
150 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/build/html/objects.inv -------------------------------------------------------------------------------- /docs/build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Search — sartopo_python 2.0.0 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 47 |
48 |
49 | 50 | 51 |
52 | 53 |

Search

54 | 55 | 63 | 64 | 65 |

66 | Searching for multiple words only shows matches that contain 67 | all words. 68 |

69 | 70 | 71 |
72 | 73 | 74 | 75 |
76 | 77 | 78 |
79 | 80 | 81 |
82 | 83 |
84 |
85 |
86 |
87 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/_static/caltopo_python_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/docs/source/_static/caltopo_python_logo.png -------------------------------------------------------------------------------- /docs/source/_templates/allpages.html: -------------------------------------------------------------------------------- 1 |
2 |

9 | 10 | -------------------------------------------------------------------------------- /docs/source/_templates/localtoc.html: -------------------------------------------------------------------------------- 1 | {# 2 | basic/localtoc.html 3 | ~~~~~~~~~~~~~~~~~~~ 4 | 5 | Sphinx sidebar template: local table of contents. 6 | 7 | :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. 8 | :license: BSD, see LICENSE for details. 9 | #} 10 | {%- if display_toc %} 11 |
12 |
13 | {{ toc }} 14 |
15 | {%- endif %} 16 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | import os 7 | import sys 8 | sys.path.insert(0, os.path.abspath('../..')) 9 | 10 | # -- Project information ----------------------------------------------------- 11 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 12 | 13 | project = 'sartopo_python' 14 | copyright = '2024, Tom Grundy' 15 | author = 'Tom Grundy' 16 | release = '2.0.0' 17 | 18 | # -- General configuration --------------------------------------------------- 19 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 20 | 21 | extensions = [ 22 | 'sphinx.ext.autodoc' 23 | ] 24 | 25 | templates_path = ['_templates'] 26 | exclude_patterns = [] 27 | 28 | 29 | 30 | # -- Options for HTML output ------------------------------------------------- 31 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 32 | 33 | html_theme = 'alabaster' 34 | html_logo = '_static/caltopo_python_logo.png' 35 | html_theme_options = { 36 | 'page_width':'85%', 37 | 'sidebar_width':'250px', 38 | 'sidebar_collapse':True, 39 | 'fixed_sidebar':True, 40 | 'logo':'caltopo_python_logo.png' 41 | } 42 | html_static_path = ['_static'] 43 | html_sidebars = { 44 | '**': [ 45 | 'allpages.html', 46 | 'localtoc.html', 47 | 'searchbox.html' 48 | ] 49 | } 50 | 51 | autoclass_content = 'both' 52 | add_module_names = False 53 | toc_object_entries_show_parents = 'hide' 54 | -------------------------------------------------------------------------------- /docs/source/credentials.rst: -------------------------------------------------------------------------------- 1 | :tocdepth: 1 2 | 3 | Credentials 4 | =========== 5 | 6 | To activate online requests, you need to determine these values: 7 | 8 | - account ID (6 characters) 9 | - credential ID (12 characters) 10 | - public key (44 characters) 11 | 12 | You can place these values in the configuration file, or you can specify them as arguments during session initialization. 13 | See examples at the bottom of this page. 14 | 15 | This section only refers to your CalTopo account credentials - it does not refer to your external account provider credentials 16 | (Google, Yahoo, MSN, Apple, etc.). **This module does not need credentials for your external account provider.** 17 | 18 | Your CalTopo account may have multiple sets of credentials. These show up in the 'Credentials' section at the bottom 19 | of the 'Your Account' dialog. 20 | 21 | To open the 'Your Account' dialog, sign in to caltopo.com then click your login ID name, to the right of 22 | 'Your Data' near the top right of the web interface. Don't worry if no credentials are listed yet. 23 | 24 | Each credential has a 'credential ID' (the 12-character code shown in the Credentials table), 25 | and a 'public key', which takes a bit more work to find. 26 | 27 | Currently, the public key is most easily determined during the process of creating a new credential. 28 | 29 | To create a new credential and to determine its credential ID and public key, follow these steps (based on the README at |sme-sartopo-mapsrv_link|): 30 | 31 | 1. Open a web page to caltopo.com. Make sure you are signed in to your account: 32 | you should see your user name or login name at the top right, to the right of 'Your Data'. 33 | 34 | 2. In a separate browser tab, go to |activation-link|. 35 | This should show a web page similar to |activation-image_link| from the |desktop-installation_link| instructions. Don't click Sync Account yet. 36 | 37 | 3. Open the developer console of your browser and start monitoring network traffic. 38 | For Chrome, use F12 to open Chrome DevTools; network traffic logging should be on when you open DevTools, 39 | as indicated by a red square-in-circle near the top left, which would stop monitoring network traffic 40 | when clicked. 41 | 42 | 4. Type 'sartopo_python' or a similar name for 'Your device will be synced as'. The exact name is not important, 43 | but can help you keep track of credentials in case you have several. Afterwards, the name you enter here will 44 | show up in the Credentials section of the Your Account dialog as above. 45 | 46 | 5. Check the checkbox and click Sync Account. (This should load an error page, which is OK.) 47 | 48 | 6. In the network traffic monitor, you will see many requests. After a few seconds, you can stop or pause 49 | network traffic monitoring to make sure the important entry does not get scrolled away as more new traffic happens. 50 | 51 | 7. In the first few requests, at the top of the list, you should see a request similar to:: 52 | 53 | finish-activate?code=........&name=...... 54 | 55 | Write down or copy the 8-character value after 'code=' from that request. This is not the value to put in the 56 | configuration file; you will use it in the next step. 57 | 58 | 8. In a new browser tab, go to:: 59 | 60 | caltopo.com/api/v1/activate?code= 61 | 62 | replacing with the 8-character code from the previous step. 63 | 64 | 9. This should load a page that looks like the following (possibly all compressed into one line): 65 | 66 | .. code-block:: json 67 | 68 | { 69 | "code": "XXXXXXXXXXX", 70 | "account": { 71 | "id": "ABC123", 72 | "type": "Feature", 73 | "properties": { 74 | "subscriptionExpires": 1554760038, 75 | "subscriptionType": "pro-1", 76 | "subscriptionRenew": true, 77 | "subscriptionStatus": "active", 78 | "title": "......@example", 79 | "class": "UserAccount", 80 | "updated": 1554760038, 81 | "email": "......@example.com" 82 | } 83 | }, 84 | "key": "xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=" 85 | } 86 | 87 | 10. Enter the 12-character 'code' value as 'id' in the configuration file. Enter the 44-character value of 'key' 88 | as 'key' in the configuration file. Enter the 6-character 'id' value as 'accountId' in the configuration file:: 89 | 90 | # sartopo_python config file 91 | # This file contains credentials used to send API map requests 92 | # to sartopo.com. Protect and do not distribute these credentials. 93 | [joe@example.com] 94 | id=XXXXXXXXXXXX 95 | key=xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX= 96 | accountId=ABC123 97 | 98 | Alternately, any of these can be specified as arguments when initializing the session, which will override values 99 | from the configuration file (if any): 100 | 101 | .. code-block:: python 102 | 103 | # to use the config file: specify filename and account name 104 | sts=SartopoSession('sartopo.com', 105 | configpath='../../sts.ini', 106 | account='joe@gmail.com') 107 | 108 | # to use arguments instead of the config file: 109 | sts=SartopoSession('sartopo.com', 110 | id='XXXXXXXXXXXX', 111 | key='xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=', 112 | accountId='ABC123') 113 | 114 | # to use the config file, but use arguments to override values from the config file: 115 | sts=SartopoSession('sartopo.com', 116 | configpath='../../sts.ini', 117 | account='joe@gmail.com', 118 | id='XXXXXXXXXXXX', 119 | key='xXXXXxXXXXXXXXXxxxXXXXxXxXXXXXXXXXXXX=', 120 | accountId='ABC123') 121 | 122 | .. |activation-link| raw:: html 123 | 124 | https://caltopo.com/app/activate/offline?redirect=localhost 125 | 126 | .. |activation-image_link| raw:: html 127 | 128 | the one used during CalTopo Desktop activation 129 | 130 | .. |desktop-installation_link| raw:: html 131 | 132 | CalTopo Desktop Installation 133 | 134 | .. |sme-sartopo-mapsrv_link| raw:: html 135 | 136 | https://github.com/elliottshane/sme-sartopo-mapsrv -------------------------------------------------------------------------------- /docs/source/fixedChoices.rst: -------------------------------------------------------------------------------- 1 | Fixed choices 2 | ------------- 3 | For some property and argument values that are specified as strings, only certain values will be processed correctly. 4 | 5 | These fixed lists of choices are determined by caltopo.com - not by the authors of this module. The lists below are (hopefully) accurate at the time of writing, but, could change at any time. 6 | 7 | The best option is to inspect outgoing network traffic while creating or editing a feature from the web interface with the selection you want. 8 | 9 | - Marker symbol name 10 | There are well over a hundred available marker symbols. Only a few common ones are listed here. Use the technique above to find the one you want. 11 | 12 | point, c:ring, c:target1, cp, heatsource, clue 13 | 14 | - Line pattern 15 | These formatted strings are too detailed to list here. Use the technique above to find the one you want. 16 | 17 | - Feature class 18 | Shape, Marker, AppTrack, LiveTrack, Folder, MapMediaObject, OperationalPeriod, Assignment, Clue, Resource, SmsLocationRequest 19 | - NOTE: Polygons and Lines are both part of the 'Shape' feature class, but are differentiated by the 'Geometry type' of 'Polygon' vs. 'LineString'. 20 | 21 | - Assignment priority 22 | HIGH, MEDIUM, LOW 23 | 24 | - Assignment POD (Responsive, Unresponsive, Clue) 25 | HIGH, MEDIUM, LOW 26 | 27 | - Assignment status 28 | DRAFT, PREPARED, INPROGRESS, COMPLETED 29 | 30 | - Assignment resource type 31 | 32 | GROUND, GROUND_1, GROUND_2, GROUND_3, DOG, DOG_TRAIL, DOG_AREA, DOG_HRD, OHV, BIKE, WATER, MOUNTED, AIR 33 | 34 | - NOTE: Underscores are used in the actual property values, but the web interface will display hyphens instead of underscores. -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. sartopo_python documentation master file, created by 2 | sphinx-quickstart on Fri May 17 19:27:57 2024. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | :tocdepth: 1 7 | 8 | .. .. toctree:: 9 | .. .. :maxdepth: 2 10 | .. :caption: Other pages: 11 | 12 | .. credentials 13 | .. sartopo_python 14 | 15 | sartopo_python 16 | ========================================== 17 | 18 | CalTopo is a very popular web-browser-based and smartphone-app-based mapping tool. SARTopo is a mostly-obsolete 19 | name that refers to a set of Search-And-Rescue-specific features inside the CalTopo tool. See |caltopo_link| and |training_link|. 20 | 21 | Being a web-based tool, CalTopo / SARTopo uses a web API to accomplish most user actions. The API is not currently documented or developed for general public use, and could change at any time. 22 | 23 | This module provides a 'session' object which manages a data connection to a hosted map, and provides several wrapper methods and convenience methods that make calls to the non-publicized CalTopo API. 24 | 25 | **This third-party module is not written or maintained by CalTopo LLC or the authors of caltopo.com or sartopo.com.** 26 | 27 | **DISCLAIMER: This module can edit and delete CalTopo / SARTopo map features. At the time of this module's publication, CalTopo and SARTopo do not have any 'undo' capability.** 28 | Only you can take steps to prevent loss of map data due to use of this module - whether due to accidental misuse, or due to an unexpected bug in the module. You should always consider exporting a full GeoJSON from your map before using this code. 29 | 30 | Categories of provided class methods: 31 | - account data access 32 | - feature creation 33 | - feature editing 34 | - feature querying 35 | - feature deletion 36 | - geometry operations 37 | 38 | See the `SartopoSession Class Reference <./sartopo_python.html>`_ for details. 39 | 40 | Installation 41 | ============ 42 | Install this package in the usual manner: 43 | 44 | .. code-block:: python 45 | 46 | pip install sartopo_python 47 | 48 | To activate online requests, you will need to determine your account ID, credential ID, and public key. See details at the :doc:`credentials` page. 49 | 50 | **NOTE: sartopo_python is changing names to caltopo_python.** 51 | caltopo_python 1.0.x will be identical to sartopo_python 2.0.x. 52 | 53 | We suggest that you change to caltopo_python as soon as possible. If you are just getting started with sartopo_python, we suggest that you use caltopo_python instead. 54 | 55 | For more information, see the :doc:`migration` page. 56 | 57 | Key Features 58 | =============== 59 | 60 | Internet or locally-hosted 61 | -------------------------- 62 | In addition to the standard caltopo.com or sartopo.com web interface, CalTopo provides a downloadable local HTTP server 63 | called CalTopo Desktop (formerly CalTopo Offline or SARTopo Offline). This module works with either solution. 64 | 65 | Configuration file 66 | ------------------ 67 | User account details, including the authentication public key (used to generate signed requests, as required by caltopo.com and sartopo.com), 68 | are kept in a local configuration file that you control. A template configuration file comes with this package. 69 | 70 | See the bottom of the Examples section for configuration file examples. See the :doc:`credentials` page for details on authentication. 71 | 72 | Mapless session 73 | --------------- 74 | You may want to initialize the session without specifying a map, e.g. if you need to start 75 | by checking the list of available maps before you know which map to open. 76 | 77 | You can open a 'mapless' session by simply omitting the mapID argument when you initialize the session. In that case, you can 78 | open a map later, within the same session, with .openMap(). 79 | 80 | Any of the 'account data access methods' will work in a mapless session. 81 | Most of the other class methods require an open map, so will fail with an error message if called in a mapless session. 82 | 83 | Local cache 84 | ----------- 85 | If the session is associated with a hosted map, this module will keep a local cache of the entire map data structure. This reduces 86 | the number of web requests needed, which reduces the time taken for most tasks. 87 | 88 | Sync with callbacks 89 | ------------------- 90 | To keep the local cache in sync with the hosted map, this module automatically requests and processes updates from the host at the specified sync interval. 91 | 92 | This sync procedure is done in a background thread, so that it doesn't delay or interfere with your code. Sync can be paused or disabled if needed. 93 | 94 | You can also write callback functions that will be called whenever map data changes are found during sync. 95 | 96 | Examples 97 | ======== 98 | 99 | Opening a session 100 | ----------------- 101 | 102 | .. code-block:: python 103 | 104 | from sartopo_python import SartopoSession 105 | 106 | # open an online session and map 107 | sts=SartopoSession('caltopo.com','A1B2C', 108 | configpath='../../sts.ini', 109 | account='joe@domain.com') 110 | 111 | # open a CalTopo Desktop session and map 112 | sts=SartopoSession('localhost:8080','A1B2C', 113 | configpath='../../sts.ini', 114 | account='joe@domain.com') 115 | 116 | # open an online mapless session 117 | sts=SartopoSession('caltopo.com', 118 | configpath='../../sts.ini', 119 | account='joe@domain.com') 120 | 121 | # open a map, for a session that was initially mapless 122 | sts.openMap('A1B2C') 123 | 124 | Syncing and callbacks 125 | --------------------- 126 | 127 | .. code-block:: python 128 | 129 | # define callback functions 130 | def pucb(*args): 131 | print('Property Updated: pucb called with args '+str(args)) 132 | 133 | def gucb(*args): 134 | print('Geometry Updated: gucb called with args '+str(args)) 135 | 136 | def nfcb(*args): 137 | print('New Feature: nfcb called with args '+str(args)) 138 | 139 | def dfcb(*args): 140 | print('Deleted Feature: dfcb called with args '+str(args)) 141 | 142 | # open a session, connecting to the defined callbacks; 143 | # syncing is enabled by default, since the 'sync' argument defaults to True 144 | sts=SartopoSession('caltopo.com','A1B2C', 145 | configpath='../../sts.ini', 146 | account='joe@domain.com', 147 | propUpdateCallback=pucb, 148 | geometryUpdateCallback=gucb, 149 | newFeatureCallback=nfcb, 150 | deletedFeatureCallback=dfcb) 151 | 152 | Getting map data and account data 153 | --------------------------------- 154 | 155 | .. code-block:: python 156 | 157 | # get the personal map list (for joe@domain.com) 158 | sts.getMapList() 159 | 160 | # get the MyTeam map list (assuming joe@domain.com is a member of MyTeam) 161 | sts.getMapList('MyTeam') 162 | 163 | # get a dict of all map lists (for joe@domain.com) 164 | sts.getAllMapLists() 165 | 166 | # get the title of a map (assuming joe@domain.com has access to the map) 167 | sts.getMapTitle('A1B2C') 168 | 169 | # get the list of titles of group accounts of which joe@domain.com is a member 170 | sts.getGroupAccountTitles() 171 | 172 | Adding features 173 | --------------- 174 | 175 | A word on longitude / latitude sequence: 176 | 177 | caltopo.com expects each point of every type of geometry to have longitude first, followed by latutude, e.g. [120,-39]. 178 | 179 | While the code will swap coordinates if needed and if detectable (which is only the case for half of the globe), it's best to get in the habit of 180 | specifying points in [lon,lat] sequence. See the *._validatePoints* documentation for details. 181 | 182 | This is opposite of the Marker functions, which call for the latitude argument first. 183 | 184 | .. code-block:: python 185 | 186 | # add a marker 187 | sts.addMarker(39,-120,'MyMarker') 188 | 189 | # add a folder 190 | fid=sts.addFolder('MyFolder') 191 | 192 | # add a marker in the folder 193 | myMarker2=sts.addMarker(39.01,-120.01,'MyMarker2',folderId=fid) 194 | 195 | # add a line 196 | sts.addLine([[39,-120],[39.1,-120.1]],'MyLine') 197 | 198 | # prepare to add a polygon - queue it for later 199 | sts.addPolygon([[39,-120],[39.1,-120.1],[39.1,-120]],'MyPolygon',queue=True) 200 | 201 | # add an Operational Period 202 | op1=sts.addOperationalPeriod('1') 203 | 204 | # prepare to add a line assignment - queue it for later 205 | aa=sts.addLineAssignment([[39.2,-120],[39.2,-120.1]], 206 | letter='AA', 207 | opId=op1, 208 | resourceType='DOG-TRAIL', 209 | description='FindEm', 210 | queue=True) 211 | 212 | sts.addAreaAssignment([[39.3,-120],[39.4,-120.1],[39.4,-120]], 213 | letter='AB', 214 | number='104', 215 | opId=op1, 216 | resourceType='DOG-AREA', 217 | description='FindEmFirst', 218 | responsivePOD='HIGH', 219 | priority='HIGH') 220 | 221 | # add the queued features now (MyPolygon and AA) 222 | sts.flush() 223 | 224 | Querying and editing features 225 | ----------------------------- 226 | 227 | .. code-block:: python 228 | 229 | myMarker=sts.getFeature('Marker','MyMarker') 230 | 231 | sts.editFeature(myMarker['id'],properties={'title','NewTitle'}) 232 | 233 | sts.moveMarker(39,-121.5,myMarker['id']) 234 | 235 | sts.editMarkerDescription('New marker description',myMarker['id']) 236 | 237 | Geometry operations 238 | ------------------- 239 | 240 | .. code-block:: python 241 | 242 | # assuming all of the named features below have already been drawn 243 | 244 | # cut area assignment AC 103, using line b0 245 | sts.cut('AC 103','b0') 246 | 247 | # cut line a1, using line b1 248 | sts.cut('a1','b1') 249 | 250 | # cut polygon a8, using polygon b8, but do not delete b8 afterwards 251 | sts.cut('a8','b8',deleteCutter=False) 252 | 253 | # arguments are ids instead of entire features 254 | sts.cut(a12['id'],b12['id']) 255 | 256 | # expand polygon a7 to include polygon b7, a.k.a. "a7 = a7 OR b7" 257 | sts.expand('a7','b7') 258 | 259 | # crop line a14 using boundary poygon b14 260 | sts.crop('a14','b14') 261 | 262 | # crop line a15 using boundary polygon b15, with zero oversize 263 | sts.crop('a15','b15',beyond=0) 264 | 265 | Deleting features 266 | ----------------- 267 | 268 | .. code-block:: python 269 | 270 | sts.delFeature(aa) 271 | 272 | sts.delMarkers([myMarker,myMarker2]) 273 | 274 | Configuration file 275 | ------------------ 276 | 277 | .. code-block:: python 278 | 279 | # sartopo_python config file 280 | # This file contains credentials used to send API map requests 281 | # to caltopo.com, sartopo.com, or CalTopo Desktop. 282 | # Protect and do not distribute these credentials. 283 | 284 | [joe@domain.com] # section referenced by 'account' session object attribute / argument 285 | id=A1B2C3D4E5F6 # 12-character credential ID 286 | key=............................................ # 44-character caltopo API key 287 | accountId=A1B2C3 # 6-character account ID 288 | 289 | 290 | .. Indices and tables 291 | .. ================== 292 | 293 | .. * :ref:`genindex` 294 | .. * :ref:`modindex` 295 | .. * :ref:`search` 296 | 297 | .. |caltopo_link| raw:: html 298 | 299 | caltopo.com 300 | 301 | .. |training_link| raw:: html 302 | 303 | training.caltopo.com 304 | 305 | 306 | -------------------------------------------------------------------------------- /docs/source/migration.rst: -------------------------------------------------------------------------------- 1 | Migration 2 | ========================================== 3 | 4 | There are two categories of migration that you might be concerned with: 5 | 6 | 1. sartopo_python is changing names to caltopo_python 7 | ----------------------------------------------------- 8 | 9 | This module began as 'sartopo_python'. There are several reasons for changing the module name, including: 10 | 11 | - most of the features in this module are not SAR-specific 12 | - sartopo.com is now effectively the same as caltopo.com; the only difference is the 'SAR' vs. 'Recreation' mode setting for each map 13 | - caltopo.com has much broader name recognition than sartopo.com outside of the SAR community 14 | - CalTopo is the name of the app; there is no 'SARTopo app' 15 | - the name of the downloadble server is CalTopo Desktop (formerly SARTopo Offline or CalTopo Offline); there is no 'SARTopo Desktop' 16 | 17 | caltopo_python 1.0.x will be identical to sartopo_python 2.0.x. 18 | 19 | sartopo_python will not receive any updates after 2.0.x. That is, there will be no sartopo_python 2.1.0. 20 | Patches / bug fixes to 1.0 / 2.0 will be applied to both packages, but, 21 | minor and major version updates will only be applied to caltopo_python. 22 | 23 | We suggest that you change to caltopo_python as soon as possible. If you are just getting started with sartopo_python, we suggest that you use caltopo_python instead. 24 | 25 | *How do I migrate from sartopo_python to caltopo_python?* 26 | 27 | 1. pip install caltopo_python 28 | 2. import caltopo_python instead of import sartopo_python 29 | 3. create an instance of CaltopoSession instead of SartopoSession 30 | 31 | There is no change to class method names or signatures. The class name is the only difference. 32 | 33 | 2. migrating to sartopo_python 2.0.0 from an earlier version 34 | ------------------------------------------------------------ 35 | 36 | (or, migrating to caltopo_python 1.0.0 from an earlier version of sartopo_python) 37 | 38 | Some class method names and signatures have changed from earlier versions. 39 | 40 | Several method names now start with an underscore, to indicate that they are not likely to be needed directly 41 | in your downstream code. These 'internal' data management and helper methods are normally only called internally 42 | by other class methods. 43 | 44 | They can still be called from your downstream code if there is a specific need, as long as you are fully aware 45 | of their impacts on internal class data - especially the local cache and the threaded queueing operations. 46 | 47 | These previously-non-underscored methods from the latest PyPi version (1.1.2) are now 'internal' (e.g. .setupSession is now ._setupSession): 48 | 49 | - ._setupSession, ._sendRequest 50 | 51 | In addition, these previously-non-underscored methods from more recent versions of source code are now 'internal': 52 | 53 | - ._sendUserdata, ._doSync, ._refresh, ._start, ._stop, ._pause, ._resume, ._buffer2, ._intersection2, ._caseMatch, ._twoify, ._fourify, ._removeDuplicatePoints, ._removeSpurs, ._getUsedSuffixList, ._getNextAvailableSuffix, ._getToken 54 | 55 | In general, the changes from the latest PyPi version (1.1.2) are extensive enough that documenting 56 | the differences would not be any more helpful than looking in the Class Reference. 57 | 58 | Changes from more recent versions of source code mainly involve method signatures (argument names, types, and sequences). 59 | 60 | In either case, migrating your code from previous versions may be a fair amount of work, but the new feature set and 61 | forward compatibility should be a good payoff. Use the Examples section of the main page, 62 | and the Class Reference, as your guides. -------------------------------------------------------------------------------- /docs/source/sartopo_python.rst: -------------------------------------------------------------------------------- 1 | :tocdepth: 3 2 | 3 | sartopo\_python module 4 | ====================== 5 | 6 | This module provides one main class: SartopoSession. Class methods are categorized and documented below. 7 | 8 | Downstream code should create and use one instance of this class. 9 | 10 | An exception class is also defined, STSException, which could be thrown during initialization but is not documented here. 11 | 12 | .. see https://stackoverflow.com/a/48682589/3577105 for categorization technique 13 | .. currentmodule:: sartopo_python 14 | 15 | .. autoclass:: SartopoSession 16 | 17 | .. omitting class name from automethod calls, when the section headers are unindented, 18 | .. causes these failures, but, the headers need to be unindented for the TOC to recognize them: 19 | .. WARNING: don't know which module to import for autodocumenting 'openMap' (try placing a "module" or "currentmodule" directive in the document, or giving an explicit module name) 20 | 21 | .. **Session setup methods** 22 | .. ------------------------- 23 | 24 | .. automethod:: SartopoSession.openMap 25 | 26 | **Account data access methods** 27 | ------------------------------- 28 | These methods may be called from either a mapless or a map-associated session. 29 | 30 | .. automethod:: SartopoSession.getAccountData 31 | .. automethod:: SartopoSession.getMapList 32 | .. automethod:: SartopoSession.getAllMapLists 33 | .. automethod:: SartopoSession.getMapTitle 34 | .. automethod:: SartopoSession.getGroupAccountTitles 35 | 36 | **Feature creation methods** 37 | ---------------------------- 38 | Most of these feature creation methods can be used to edit an existing feature, 39 | by specifying the existingId argument. .editFeature is a convenience method 40 | that calls the appropriate .add... method with existingId specified. 41 | 42 | .. automethod:: SartopoSession.addFolder 43 | .. automethod:: SartopoSession.addMarker 44 | .. automethod:: SartopoSession.addLine 45 | .. automethod:: SartopoSession.addPolygon 46 | .. automethod:: SartopoSession.addOperationalPeriod 47 | .. automethod:: SartopoSession.addLineAssignment 48 | .. automethod:: SartopoSession.addAreaAssignment 49 | .. automethod:: SartopoSession.addAppTrack 50 | .. automethod:: SartopoSession.flush 51 | 52 | **Feature query methods** 53 | ------------------------- 54 | 55 | .. automethod:: SartopoSession.getFeature 56 | .. automethod:: SartopoSession.getFeatures 57 | 58 | **Feature editing methods** 59 | --------------------------- 60 | 61 | .. automethod:: SartopoSession.editFeature 62 | .. automethod:: SartopoSession.moveMarker 63 | .. automethod:: SartopoSession.editMarkerDescription 64 | 65 | **Feature deletion methods** 66 | ---------------------------- 67 | 68 | .. automethod:: SartopoSession.delFeature 69 | .. automethod:: SartopoSession.delFeatures 70 | .. automethod:: SartopoSession.delMarker 71 | .. automethod:: SartopoSession.delMarkers 72 | 73 | **Geometry operation methods** 74 | ------------------------------ 75 | These methods use the python |shapely_link| module. 76 | 77 | .. automethod:: SartopoSession.cut 78 | .. automethod:: SartopoSession.expand 79 | .. automethod:: SartopoSession.crop 80 | .. automethod:: SartopoSession.getBounds 81 | 82 | **Internal data management methods** 83 | ------------------------------------ 84 | These methods are typically only called internally, from other class methods. They can be called from downstream code if needed, with caution. 85 | 86 | .. automethod:: SartopoSession._setupSession 87 | .. automethod:: SartopoSession._sendUserdata 88 | .. automethod:: SartopoSession._doSync 89 | .. automethod:: SartopoSession._refresh 90 | .. automethod:: SartopoSession.__del__ 91 | .. automethod:: SartopoSession._start 92 | .. automethod:: SartopoSession._stop 93 | .. automethod:: SartopoSession._pause 94 | .. automethod:: SartopoSession._resume 95 | .. automethod:: SartopoSession._syncLoop 96 | .. automethod:: SartopoSession._sendRequest 97 | .. automethod:: SartopoSession._delAsync 98 | .. automethod:: SartopoSession._buffer2 99 | .. automethod:: SartopoSession._intersection2 100 | 101 | **Internal helper methods** 102 | --------------------------- 103 | These methods are typically only called internally, from other class methods. They can be called from downstream code if needed, with caution. 104 | 105 | .. automethod:: SartopoSession._caseMatch 106 | .. automethod:: SartopoSession._twoify 107 | .. automethod:: SartopoSession._fourify 108 | .. automethod:: SartopoSession._removeDuplicatePoints 109 | .. automethod:: SartopoSession._removeSpurs 110 | .. automethod:: SartopoSession._getUsedSuffixList 111 | .. automethod:: SartopoSession._getNextAvailableSuffix 112 | .. automethod:: SartopoSession._validatePoints 113 | .. automethod:: SartopoSession._getToken 114 | 115 | .. |shapely_link| raw:: html 116 | 117 | Shapely -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/requirements.txt -------------------------------------------------------------------------------- /sartopo_python.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: sartopo-python 3 | Version: 1.1.2 4 | Summary: Python interface to unofficial SARTopo API 5 | Home-page: https://github.com/ncssar/sartopo_python 6 | Author: Tom Grundy 7 | Author-email: nccaves@yahoo.com 8 | License: UNKNOWN 9 | Download-URL: https://github.com/ncssar/sartopo_python/archive/1.1.2.tar.gz 10 | Description: # sartopo_python 11 | 12 | Sartopo / Caltopo currently does not have a publically available API; 13 | this code calls the non-publicized API that could change at any time. 14 | 15 | This module is intended to provide a simple, API-version-agnostic sartopo 16 | interface to other appliactions. 17 | 18 | This python code is in no way supported or maintained by caltopo LLC 19 | or the authors of caltopo.com or sartopo.com. 20 | 21 | ## New for v1.1.0. 5-30-2020: 22 | Signed requests for online maps are now supported. The same requests that work here for offline maps are now also working for online maps. See the signed request explanation at the bottom of this README file. 23 | 24 | ## New for v1.0.6 3-30-2020: 25 | getFeatures now returns the entire json object for each feature. The top level keys should be id, geometry, and properties. This allows preservation of things like marker-symbol, folder, coordinates, and so on when moving an existing marker. 26 | 27 | ## Installation: 28 | ``` 29 | pip install sartopo_python 30 | ``` 31 | 32 | ## Provided functions in class SartopoSession: 33 | ### \_\_init\_\_ - create a new session 34 | - domainAndPort="localhost:8080" 35 | - mapID[required]=None 36 | 37 | The remaining arguments are only needed to generate signed requests for online maps at sartopo.com; see the signed request explanation at the bottom of this README file. 38 | - configpath 39 | - account 40 | - id 41 | - key 42 | - expires 43 | ### getFeatures - get a list of map features 44 | - featureClass=None - "Marker" etc to return only markers 45 | - since=0 - get features only since this timestamp 46 | ### addFolder - create a SARTopo folder 47 | - label="New Folder" 48 | ### addMarker - create a SARTopo marker 49 | - lat 50 | - lon 51 | - title="New Marker" 52 | - description="" 53 | - color="FF0000" 54 | - symbol="point" 55 | - rotation=None 56 | - folderId=None 57 | - (beginning in 1.0.4) existingId="" - specify this to edit an existing marker 58 | ## EXAMPLES: 59 | ``` 60 | # from sartopo_python import SartopoSession 61 | # import time 62 | # 63 | # sts=SartopoSession("localhost:8080","") 64 | # fid=sts.addFolder("MyFolder") 65 | # sts.addMarker(39,-120,"stuff") 66 | # sts.addMarker(39.01,-120.01,"myStuff",folderId=fid) 67 | # r=sts.getFeatures("Marker") 68 | # print("r:"+str(r)) 69 | # print("moving the marker after a pause:"+r[0]['id']) 70 | # time.sleep(5) 71 | # sts.addMarker(39.02,-120.02,r[0]['properties']['title'],existingId=r[0]['id']) 72 | # 73 | # sts2=SartopoSession( 74 | # "sartopo.com", 75 | # "", 76 | # configpath="../../sts.ini", 77 | # account="") 78 | # fid2=sts2.addFolder("MyOnlineFolder") 79 | # sts2.addMarker(39,-120,"onlineStuff") 80 | # sts2.addMarker(39.01,-119.99,"onlineStuff2",folderId=fid2) 81 | # r2=sts2.getFeatures("Marker") 82 | # print("return value from getFeatures('Marker'):") 83 | # print(json.dumps(r2,indent=3)) 84 | # time.sleep(15) 85 | # print("moving online after a pause:"+r2[0]['id']) 86 | # sts2.addMarker(39.02,-119.98,r2[0]['properties']['title'],existingId=r2[0]['id']) 87 | ``` 88 | ## Signed Requests 89 | Requests to localhost do not require any authentication; requests to sartopo.com do require authentication in the form of request signatures. 90 | 91 | If the sartopo session object was created with 'sartopo.com' as part of the URL, then this module will sign all requests before sending. 92 | 93 | Authenticaion information required to generate the signed requests includes an account expiration timestamp, a public key, and an ID code. For a good explanation of how to determine those three items, see the README at https://github.com/elliottshane/sme-sartopo-mapsrv. 94 | 95 | Once those three items are determined, they should be stored in a configparser-compatible file that should look like the following: 96 | ``` 97 | # sartopo_pyhton config file 98 | # This file contains credentials used to send API map requests 99 | # to sartopo.com. Protect and do not distribute these credentials. 100 | 101 | [myaccount@gmail.com] 102 | id=123456ABCDEF 103 | key=aBcDeF12345somepublickey 104 | expires=1234567890 105 | ``` 106 | An explanation of how the SartopoSession constructor arguments are used to determine credentials, if sartopo.com is in the URL: 107 | ``` 108 | # if configpath and account are specified, 109 | # conigpath must be the full pathname of a configparser-compliant 110 | # config file, and account must be the name of a section within it, 111 | # containing keys 'id', 'key', and 'expires'. 112 | # otherwise, those parameters must have been specified in this object's 113 | # constructor. 114 | # if both are specified, first the config section is read and then 115 | # any parameters of this object are used to override the config file 116 | # values. 117 | # if any of those three values are still not specified, abort. 118 | ``` 119 | 120 | 121 | Platform: UNKNOWN 122 | Classifier: Programming Language :: Python :: 3 123 | Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) 124 | Classifier: Operating System :: OS Independent 125 | Classifier: Development Status :: 4 - Beta 126 | Description-Content-Type: text/markdown 127 | -------------------------------------------------------------------------------- /sartopo_python.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | README.md 2 | setup.cfg 3 | setup.py 4 | sartopo_python/__init__.py 5 | sartopo_python/sartopo_python.py 6 | sartopo_python.egg-info/PKG-INFO 7 | sartopo_python.egg-info/SOURCES.txt 8 | sartopo_python.egg-info/dependency_links.txt 9 | sartopo_python.egg-info/requires.txt 10 | sartopo_python.egg-info/top_level.txt -------------------------------------------------------------------------------- /sartopo_python.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sartopo_python.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /sartopo_python.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | sartopo_python 2 | -------------------------------------------------------------------------------- /sartopo_python/__init__.py: -------------------------------------------------------------------------------- 1 | from sartopo_python.sartopo_python import SartopoSession -------------------------------------------------------------------------------- /sartopo_python/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/sartopo_python/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /sartopo_python/__pycache__/sartopo_python.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ncssar/sartopo_python/1a4b3bb56c015fad36af7652ac9532f128a5c2c3/sartopo_python/__pycache__/sartopo_python.cpython-37.pyc -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | ###### setup.py package setup script for pyCalTopo ###### 2 | 3 | import setuptools 4 | 5 | with open("README.md", "r") as fh: 6 | long_description = fh.read() 7 | 8 | setuptools.setup( 9 | name="sartopo_python", 10 | version="2.0.0", 11 | author="Tom Grundy", 12 | author_email="nccaves@yahoo.com", 13 | description="Python interface to unofficial CalTopo/SARTopo API", 14 | long_description=long_description, 15 | long_description_content_type="text/markdown", 16 | url="https://github.com/ncssar/sartopo_python", 17 | packages=setuptools.find_packages(), 18 | download_url="https://github.com/ncssar/sartopo_python/archive/2.0.0.tar.gz", 19 | install_requires=[ 20 | 'Shapely>=2.0.2', 21 | 'requests' 22 | ], 23 | classifiers=[ 24 | "Programming Language :: Python :: 3", 25 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 26 | "Operating System :: OS Independent", 27 | "Development Status :: 4 - Beta", 28 | ], 29 | ) 30 | --------------------------------------------------------------------------------