forked from expo/troggle
1626/1623 detection and tests
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="windows-1252"?>
|
||||
<gpx version="1.0" creator="survex 1.4.1 (aven) - https://survex.com/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
|
||||
<name>1626/3-as-trk</name>
|
||||
<desc>just the 1623/6 border</desc>
|
||||
<trk>
|
||||
<name>1626/3-as-trk</name>
|
||||
<number>1</number>
|
||||
<trkseg>
|
||||
|
||||
|
||||
</trkseg>
|
||||
<trkpt lon="13.72476763" lat="47.69048595"><name>1626-22</name></trkpt>
|
||||
<trkpt lon="13.72501998" lat="47.69039518"><name>1626-21</name></trkpt>
|
||||
<trkpt lon="13.72590820" lat="47.68982018"><name>1626-20</name></trkpt>
|
||||
<trkpt lon="13.72677225" lat="47.68921909"><name>1626-19</name></trkpt>
|
||||
<trkpt lon="13.73015049" lat="47.68801941"><name>1626-18</name></trkpt>
|
||||
<trkpt lon="13.73155441" lat="47.68747860"><name>1626-17</name></trkpt>
|
||||
<trkpt lon="13.73268210" lat="47.68670564"><name>1626-16</name></trkpt>
|
||||
<trkpt lon="13.73337630" lat="47.68604764"><name>1626-15</name></trkpt>
|
||||
<trkpt lon="13.73426370" lat="47.68554896"><name>1626-14</name></trkpt>
|
||||
<trkpt lon="13.73512238" lat="47.68532296"><name>1626-13</name></trkpt>
|
||||
<trkpt lon="13.73600521" lat="47.68529913"><name>1626-12</name></trkpt>
|
||||
<trkpt lon="13.73748868" lat="47.68559800"><name>1626-11</name></trkpt>
|
||||
<trkpt lon="13.73898788" lat="47.68569259"><name>1626-10</name></trkpt>
|
||||
<trkpt lon="13.74023471" lat="47.68536616"><name>1626-9</name></trkpt>
|
||||
<trkpt lon="13.74123284" lat="47.68474835"><name>1626-8</name></trkpt>
|
||||
<trkpt lon="13.74225034" lat="47.68387240"><name>1626-7</name></trkpt>
|
||||
<trkpt lon="13.74347406" lat="47.68315752"><name>1626-6</name></trkpt>
|
||||
<trkpt lon="13.74703874" lat="47.68220764"><name>1626-5</name></trkpt>
|
||||
<trkpt lon="13.74868173" lat="47.68196967"><name>1626-4</name></trkpt>
|
||||
<trkpt lon="13.75033548" lat="47.68175412"><name>1626-3</name></trkpt>
|
||||
<trkpt lon="13.75123753" lat="47.68162591"><name>1626-2</name></trkpt>
|
||||
<trkpt lon="13.75207758" lat="47.68141505"><name>1626-1</name></trkpt>
|
||||
<trkpt lon="13.75343505" lat="47.68061505"><name>1626-323</name></trkpt>
|
||||
<trkpt lon="13.75465350" lat="47.67959704"><name>1626-322</name></trkpt>
|
||||
<trkpt lon="13.75557245" lat="47.67959630"><name>1626-321</name></trkpt>
|
||||
<trkpt lon="13.75654897" lat="47.67963415"><name>1626-320</name></trkpt>
|
||||
<trkpt lon="13.75823156" lat="47.67969809"><name>1626-319</name></trkpt>
|
||||
<trkpt lon="13.75990247" lat="47.67964786"><name>1626-318</name></trkpt>
|
||||
<trkpt lon="13.76114115" lat="47.67949668"><name>1626-317</name></trkpt>
|
||||
<trkpt lon="13.76230278" lat="47.67920634"><name>1626-316</name></trkpt>
|
||||
<trkpt lon="13.76356009" lat="47.67856495"><name>1626-315</name></trkpt>
|
||||
<trkpt lon="13.76450809" lat="47.67772225"><name>1626-314</name></trkpt>
|
||||
<trkpt lon="13.76486443" lat="47.67683862"><name>1626-313</name></trkpt>
|
||||
<trkpt lon="13.76527297" lat="47.67598354"><name>1626-312</name></trkpt>
|
||||
<trkpt lon="13.76599945" lat="47.67558241"><name>1626-311</name></trkpt>
|
||||
<trkpt lon="13.76642282" lat="47.67546486"><name>1626-310</name></trkpt>
|
||||
<trkpt lon="13.76818947" lat="47.67486721"><name>1626-309</name></trkpt>
|
||||
<trkpt lon="13.76998479" lat="47.67441976"><name>1626-308</name></trkpt>
|
||||
<trkpt lon="13.77091587" lat="47.67412570"><name>1626-307</name></trkpt>
|
||||
<trkpt lon="13.77186645" lat="47.67408972"><name>1626-306</name></trkpt>
|
||||
<trkpt lon="13.77271376" lat="47.67446428"><name>1626-305</name></trkpt>
|
||||
<trkpt lon="13.77335365" lat="47.67502035"><name>1626-304</name></trkpt>
|
||||
<trkpt lon="13.77422818" lat="47.67720260"><name>1626-303</name></trkpt>
|
||||
<trkpt lon="13.77422109" lat="47.67810825"><name>1623-77</name></trkpt>
|
||||
<trkpt lon="13.77446007" lat="47.67897442"><name>1623-78</name></trkpt>
|
||||
<trkpt lon="13.77545908" lat="47.68010650"><name>1623-79</name></trkpt>
|
||||
<trkpt lon="13.77660561" lat="47.68118866"><name>1623-80</name></trkpt>
|
||||
<trkpt lon="13.77660561" lat="47.68118866"><name>1626-299</name></trkpt>
|
||||
<trkpt lon="13.77914299" lat="47.68294347"><name>1623-81</name></trkpt>
|
||||
<trkpt lon="13.77914299" lat="47.68294347"><name>1626-298</name></trkpt>
|
||||
<trkpt lon="13.78220453" lat="47.68446155"><name>1623-82</name></trkpt>
|
||||
<trkpt lon="13.78220453" lat="47.68446155"><name>1626-297</name></trkpt>
|
||||
<trkpt lon="13.78519802" lat="47.68608852"><name>1623-83</name></trkpt>
|
||||
<trkpt lon="13.78519802" lat="47.68608852"><name>1626-296</name></trkpt>
|
||||
<trkpt lon="13.78804022" lat="47.68759918"><name>1623-84</name></trkpt>
|
||||
<trkpt lon="13.78804022" lat="47.68759918"><name>1626-295</name></trkpt>
|
||||
<trkpt lon="13.79091566" lat="47.68909077"><name>1623-85</name></trkpt>
|
||||
<trkpt lon="13.79091566" lat="47.68909077"><name>1626-294</name></trkpt>
|
||||
<trkpt lon="13.79397916" lat="47.69043850"><name>1623-86</name></trkpt>
|
||||
<trkpt lon="13.79397916" lat="47.69043850"><name>1626-293</name></trkpt>
|
||||
<trkpt lon="13.79770284" lat="47.69140571"><name>1623-87</name></trkpt>
|
||||
<trkpt lon="13.79770284" lat="47.69140571"><name>1626-292</name></trkpt>
|
||||
<trkpt lon="13.80090280" lat="47.69218602"><name>1623-88</name></trkpt>
|
||||
<trkpt lon="13.80090280" lat="47.69218602"><name>1626-291</name></trkpt>
|
||||
<trkpt lon="13.80408569" lat="47.69312830"><name>1623-89</name></trkpt>
|
||||
<trkpt lon="13.80408569" lat="47.69312830"><name>1626-290</name></trkpt>
|
||||
<trkpt lon="13.80767474" lat="47.69419465"><name>1623-90</name></trkpt>
|
||||
<trkpt lon="13.80767474" lat="47.69419465"><name>1626-289</name></trkpt>
|
||||
<trkpt lon="13.81114611" lat="47.69504470"><name>1623-91</name></trkpt>
|
||||
<trkpt lon="13.81114611" lat="47.69504470"><name>1626-288</name></trkpt>
|
||||
<trkpt lon="13.81454852" lat="47.69606670"><name>1623-92</name></trkpt>
|
||||
<trkpt lon="13.81454852" lat="47.69606670"><name>1626-287</name></trkpt>
|
||||
<trkpt lon="13.81577650" lat="47.69675143"><name>1623-93</name></trkpt>
|
||||
<trkpt lon="13.81577650" lat="47.69675143"><name>1626-286</name></trkpt>
|
||||
<trkpt lon="13.81676629" lat="47.69761518"><name>1623-94</name></trkpt>
|
||||
<trkpt lon="13.81676629" lat="47.69761518"><name>1626-285</name></trkpt>
|
||||
<trkpt lon="13.81854114" lat="47.69981221"><name>1623-95</name></trkpt>
|
||||
<trkpt lon="13.81854114" lat="47.69981221"><name>1626-284</name></trkpt>
|
||||
<trkpt lon="13.81950232" lat="47.70218349"><name>1623-96</name></trkpt>
|
||||
<trkpt lon="13.81950232" lat="47.70218349"><name>1626-283</name></trkpt>
|
||||
<trkpt lon="13.81982069" lat="47.70317869"><name>1623-97</name></trkpt>
|
||||
<trkpt lon="13.81982069" lat="47.70317869"><name>1626-282</name></trkpt>
|
||||
<trkpt lon="13.82043141" lat="47.70409618"><name>1623-98</name></trkpt>
|
||||
<trkpt lon="13.82043141" lat="47.70409618"><name>1626-281</name></trkpt>
|
||||
<trkpt lon="13.82122594" lat="47.70471970"><name>1623-99</name></trkpt>
|
||||
<trkpt lon="13.82122594" lat="47.70471970"><name>1626-280</name></trkpt>
|
||||
<trkpt lon="13.82224271" lat="47.70515970"><name>1623-100</name></trkpt>
|
||||
<trkpt lon="13.82224271" lat="47.70515970"><name>1626-279</name></trkpt>
|
||||
<trkpt lon="13.82402883" lat="47.70536617"><name>1623-101</name></trkpt>
|
||||
<trkpt lon="13.82402883" lat="47.70536617"><name>1626-278</name></trkpt>
|
||||
<trkpt lon="13.82586781" lat="47.70524409"><name>1623-102</name></trkpt>
|
||||
<trkpt lon="13.82586781" lat="47.70524409"><name>1626-277</name></trkpt>
|
||||
<trkpt lon="13.82920653" lat="47.70492586"><name>1623-103</name></trkpt>
|
||||
<trkpt lon="13.82920653" lat="47.70492586"><name>1626-276</name></trkpt>
|
||||
<trkpt lon="13.83102900" lat="47.70489099"><name>1623-104</name></trkpt>
|
||||
<trkpt lon="13.83102900" lat="47.70489099"><name>1626-275</name></trkpt>
|
||||
<trkpt lon="13.83240838" lat="47.70463501"><name>1623-105</name></trkpt>
|
||||
<trkpt lon="13.83240838" lat="47.70463501"><name>1626-274</name></trkpt>
|
||||
<trkpt lon="13.83327985" lat="47.70436234"><name>1623-106</name></trkpt>
|
||||
<trkpt lon="13.83327985" lat="47.70436234"><name>1626-273</name></trkpt>
|
||||
<trkpt lon="13.83412633" lat="47.70441440"><name>1623-107</name></trkpt>
|
||||
<trkpt lon="13.83412633" lat="47.70441440"><name>1626-272</name></trkpt>
|
||||
<trkpt lon="13.83813615" lat="47.70460471"><name>1623-108</name></trkpt>
|
||||
<trkpt lon="13.83813615" lat="47.70460471"><name>1626-271</name></trkpt>
|
||||
<trkpt lon="13.84151391" lat="47.70418600"><name>1623-109</name></trkpt>
|
||||
<trkpt lon="13.84151391" lat="47.70418600"><name>1626-270</name></trkpt>
|
||||
<trkpt lon="13.84313423" lat="47.70406198"><name>1623-110</name></trkpt>
|
||||
<trkpt lon="13.84313423" lat="47.70406198"><name>1626-269</name></trkpt>
|
||||
<trkpt lon="13.84477407" lat="47.70401966"><name>1623-111</name></trkpt>
|
||||
<trkpt lon="13.84477407" lat="47.70401966"><name>1626-268</name></trkpt>
|
||||
<trkpt lon="13.84557828" lat="47.70383169"><name>1623-112</name></trkpt>
|
||||
<trkpt lon="13.84557828" lat="47.70383169"><name>1626-267</name></trkpt>
|
||||
<trkpt lon="13.84638546" lat="47.70367164"><name>1623-113</name></trkpt>
|
||||
<trkpt lon="13.84638546" lat="47.70367164"><name>1626-266</name></trkpt>
|
||||
<trkpt lon="13.84734801" lat="47.70371947"><name>1623-114</name></trkpt>
|
||||
<trkpt lon="13.84734801" lat="47.70371947"><name>1626-265</name></trkpt>
|
||||
<trkpt lon="13.84830585" lat="47.70370893"><name>1623-115</name></trkpt>
|
||||
<trkpt lon="13.84830585" lat="47.70370893"><name>1626-264</name></trkpt>
|
||||
<trkpt lon="13.84978004" lat="47.70334887"><name>1623-116</name></trkpt>
|
||||
<trkpt lon="13.84978004" lat="47.70334887"><name>1626-263</name></trkpt>
|
||||
<trkpt lon="13.85115245" lat="47.70281564"><name>1623-117</name></trkpt>
|
||||
<trkpt lon="13.85115245" lat="47.70281564"><name>1626-262</name></trkpt>
|
||||
<trkpt lon="13.85459377" lat="47.70171281"><name>1623-118</name></trkpt>
|
||||
<trkpt lon="13.85459377" lat="47.70171281"><name>1626-261</name></trkpt>
|
||||
<trkpt lon="13.85613331" lat="47.70138292"><name>1623-119</name></trkpt>
|
||||
<trkpt lon="13.85613331" lat="47.70138292"><name>1626-260</name></trkpt>
|
||||
<trkpt lon="13.85761344" lat="47.70097144"><name>1623-120</name></trkpt>
|
||||
<trkpt lon="13.85761344" lat="47.70097144"><name>1626-259</name></trkpt>
|
||||
<trkpt lon="13.85887862" lat="47.70064823"><name>1623-121</name></trkpt>
|
||||
<trkpt lon="13.85887862" lat="47.70064823"><name>1626-258</name></trkpt>
|
||||
<trkpt lon="13.85975787" lat="47.70038236"><name>1623-122</name></trkpt>
|
||||
<trkpt lon="13.85975787" lat="47.70038236"><name>1626-257</name></trkpt>
|
||||
<trkpt lon="13.86031535" lat="47.70002466"><name>1623-123</name></trkpt>
|
||||
<trkpt lon="13.86031535" lat="47.70002466"><name>1626-256</name></trkpt>
|
||||
</trk>
|
||||
</gpx>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='windows-1252'?>
|
||||
<gpx version="1.1" creator="CaveAreaProcessor" xmlns="http://www.topografix.com/GPX/1/1">
|
||||
<trk>
|
||||
<name>Cleaned Border 1626-1623</name>
|
||||
<trkseg />
|
||||
</trk>
|
||||
</gpx>
|
||||
@@ -0,0 +1,80 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from position_utils import which_area # file-type import, not module type.
|
||||
|
||||
def run_limit_tests(which_area_func):
|
||||
# Boundary limits derived from GPX data
|
||||
west_limit = 13.72476763 # 1626-22 [cite: 1]
|
||||
east_limit = 13.86031535 # 1626-256 [cite: 3]
|
||||
|
||||
print("Running Border Logic Tests...")
|
||||
|
||||
# 1. Invalid point to the West
|
||||
res, area = which_area_func(47.69, west_limit - 0.01)
|
||||
print(f"Test West: Expected (False, 'None'), Got ({res}, '{area}')")
|
||||
|
||||
# 2. Invalid point to the East
|
||||
res, area = which_area_func(47.70, east_limit + 0.01)
|
||||
print(f"Test East: Expected (False, 'None'), Got ({res}, '{area}')")
|
||||
|
||||
|
||||
def generate_test_data():
|
||||
offset = 0.0005
|
||||
|
||||
# 6 base points extracted from the track to ensure we cover the whole E/W span
|
||||
# Coordinates from source GPX
|
||||
|
||||
base_coords = [
|
||||
(47.69048595, 13.72476763 + 2* offset), # Far West (1626-22)
|
||||
(47.68532296, 13.73512238), # Monotonic section 1 (1626-13)
|
||||
(47.67441976, 13.76998479), # Central dip (1626-308)
|
||||
(47.68909077, 13.79091566), # Rising section (1626-294)
|
||||
(47.70536617, 13.82402883), # Northern peak (1626-278)
|
||||
(47.70002466, 13.86031535 - 2* offset) # Far East (1626-256)
|
||||
]
|
||||
|
||||
test_points = []
|
||||
|
||||
for lat, lon in base_coords:
|
||||
# Generate North point (Area 1626)
|
||||
test_points.append({'lat': lat + offset, 'lon': lon + offset, 'area': '1626', 'desc': 'North_Test'})
|
||||
# Generate South point (Area 1623)
|
||||
test_points.append({'lat': lat - offset, 'lon': lon - offset, 'area': '1623', 'desc': 'South_Test'})
|
||||
|
||||
return test_points
|
||||
|
||||
def export_test_gpx(test_points, filename="test_points.gpx"):
|
||||
gpx = ET.Element("gpx", version="1.1", creator="Python Script",
|
||||
xmlns="http://www.topografix.com/GPX/1/1")
|
||||
|
||||
for pt in test_points:
|
||||
wpt = ET.SubElement(gpx, "wpt", lat=str(pt['lat']), lon=str(pt['lon']))
|
||||
name = ET.SubElement(wpt, "name")
|
||||
name.text = f"{pt['area']}_{pt['desc']}"
|
||||
desc = ET.SubElement(wpt, "desc")
|
||||
desc.text = f"Expected Area: {pt['area']}"
|
||||
|
||||
tree = ET.ElementTree(gpx)
|
||||
tree.write(filename, encoding="utf-8", xml_declaration=True)
|
||||
print(f"Successfully created {filename} with 12 test waypoints.")
|
||||
|
||||
def run_12_point_test(which_area_func):
|
||||
points = generate_test_data()
|
||||
export_test_gpx(points)
|
||||
|
||||
passed = 0
|
||||
|
||||
print(f"{'Lat':<12} | {'Lon':<12} | {'Expected':<10} | {'Result'}")
|
||||
print("-" * 55)
|
||||
|
||||
for pt in points:
|
||||
valid, area = which_area_func(pt['lat'], pt['lon'])
|
||||
status = "PASS" if (valid and area == pt['area']) else "FAIL"
|
||||
if status == "PASS": passed += 1
|
||||
print(f"{pt['lat']:<12.6f} | {pt['lon']:<12.6f} | {pt['area']:<10} | {status}")
|
||||
|
||||
print(f"\nSummary: {passed}/12 points passed.")
|
||||
|
||||
# Uncomment to run:
|
||||
run_limit_tests(which_area)
|
||||
run_12_point_test(which_area)
|
||||
@@ -0,0 +1,184 @@
|
||||
import bisect
|
||||
from pathlib import Path
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
def load_and_clean_gpx(filename):
|
||||
if not Path(filename).exists:
|
||||
print("No file")
|
||||
else:
|
||||
print(f"Loading {filename}")
|
||||
# Parse GPX
|
||||
tree = ET.parse(filename)
|
||||
root = tree.getroot()
|
||||
|
||||
# Extract points (handling namespaces)
|
||||
ns = {'gpx': 'http://www.topografix.com/GPX/1/0'}
|
||||
raw_points = []
|
||||
for pt in root.findall('.//gpx:trkpt', ns):
|
||||
lat = float(pt.get('lat'))
|
||||
lon = float(pt.get('lon'))
|
||||
raw_points.append((lon, lat)) # Using (x, y) order for easier logic
|
||||
|
||||
n_raw = len(raw_points)
|
||||
if not raw_points:
|
||||
return []
|
||||
|
||||
# 1. Remove exactly duplicate consecutive points
|
||||
cleaned_points = [raw_points[0]]
|
||||
for i in range(1, len(raw_points)):
|
||||
if raw_points[i] != raw_points[i-1]:
|
||||
cleaned_points.append(raw_points[i])
|
||||
|
||||
n_clean =len(cleaned_points)
|
||||
print(f"read {n_raw} points and returned {n_clean} cleaned points")
|
||||
return cleaned_points
|
||||
|
||||
|
||||
|
||||
def save_cleaned_gpx(points, output_filename="cleaned_border.gpx"):
|
||||
"""
|
||||
Saves a list of (lon, lat) tuples as a GPX track.
|
||||
"""
|
||||
# Create the root element with necessary namespaces
|
||||
root = ET.Element("gpx",
|
||||
version="1.1",
|
||||
creator="CaveAreaProcessor",
|
||||
xmlns="http://www.topografix.com/GPX/1/1")
|
||||
|
||||
# Create the track structure
|
||||
trk = ET.SubElement(root, "trk")
|
||||
name = ET.SubElement(trk, "name")
|
||||
name.text = "Cleaned Border 1626-1623"
|
||||
|
||||
trkseg = ET.SubElement(trk, "trkseg")
|
||||
|
||||
# Add each point as a track point
|
||||
for lon, lat in points:
|
||||
# Note: GPX uses lat/lon attributes, we provide them as strings
|
||||
ET.SubElement(trkseg, "trkpt", lat=str(lat), lon=str(lon))
|
||||
|
||||
# Write to file
|
||||
tree = ET.ElementTree(root)
|
||||
# Use indenting for readability if available (Python 3.9+)
|
||||
if hasattr(ET, "indent"):
|
||||
ET.indent(tree, space="\t", level=0)
|
||||
|
||||
tree.write(output_filename, encoding="windows-1252", xml_declaration=True)
|
||||
print(f"Cleaned track exported to {output_filename}")
|
||||
|
||||
|
||||
def split_into_monotonic_segments(points):
|
||||
"""
|
||||
Splits the track into segments that are monotonic in Longitude (E/W).
|
||||
This allows us to use binary search on each segment.
|
||||
"""
|
||||
if not points: return []
|
||||
|
||||
segments = []
|
||||
current_segment = [points[0]]
|
||||
|
||||
for i in range(1, len(points)):
|
||||
p_prev = points[i-1]
|
||||
p_curr = points[i]
|
||||
|
||||
# Check if we should start a new segment based on direction change in Lon
|
||||
if len(current_segment) > 1:
|
||||
prev_dir = current_segment[-1][0] - current_segment[-2][0]
|
||||
curr_dir = p_curr[0] - p_prev[0]
|
||||
|
||||
# If direction changed (and wasn't zero before), split
|
||||
if (prev_dir > 0 and curr_dir < 0) or (prev_dir < 0 and curr_dir > 0):
|
||||
segments.append(current_segment)
|
||||
current_segment = [p_prev]
|
||||
|
||||
current_segment.append(p_curr)
|
||||
|
||||
segments.append(current_segment)
|
||||
print(len(segments))
|
||||
return segments
|
||||
|
||||
|
||||
|
||||
|
||||
PREPARED_SEGMENTS = []
|
||||
MIN_LON = 0
|
||||
MAX_LON = 0
|
||||
|
||||
def generate_boundary_segments():
|
||||
# Example Workflow:
|
||||
points = load_and_clean_gpx('1623-6_border.gpx')
|
||||
# save_cleaned_gpx(points, "cleaned_border_output.gpx") # done once, not needed
|
||||
mono_segments = split_into_monotonic_segments(points)
|
||||
|
||||
# Pre-calculate global bounds and segment metadata
|
||||
ALL_LONS = [p[0] for p in points]
|
||||
MIN_LON, MAX_LON = min(ALL_LONS), max(ALL_LONS)
|
||||
|
||||
# Prepare segments for binary search
|
||||
# We store each segment as (sorted_lons, corresponding_lats)
|
||||
for seg in mono_segments:
|
||||
lons = [p[0] for p in seg]
|
||||
lats = [p[1] for p in seg]
|
||||
|
||||
# Ensure lons are strictly increasing for bisect
|
||||
is_increasing = lons[-1] > lons[0]
|
||||
if not is_increasing:
|
||||
lons.reverse()
|
||||
lats.reverse()
|
||||
|
||||
PREPARED_SEGMENTS.append({
|
||||
'lons': lons,
|
||||
'lats': lats,
|
||||
'min_lon': min(lons),
|
||||
'max_lon': max(lons)
|
||||
})
|
||||
return MIN_LON, MAX_LON
|
||||
|
||||
def which_area(lat, lon):
|
||||
global PREPARED_SEGMENTS, MIN_LON, MAX_LON
|
||||
|
||||
if not PREPARED_SEGMENTS:
|
||||
MIN_LON, MAX_LON = generate_boundary_segments()
|
||||
|
||||
# Fast boundary check (East/West limits)
|
||||
if lon < MIN_LON or lon > MAX_LON:
|
||||
return False, "None"
|
||||
|
||||
# Area 1626 is North, Area 1623 is South
|
||||
# We find the boundary latitude at this longitude
|
||||
boundary_lat = None
|
||||
|
||||
for seg in PREPARED_SEGMENTS:
|
||||
# Check if lon is within this monotonic segment
|
||||
if seg['min_lon'] <= lon <= seg['max_lon']:
|
||||
lons = seg['lons']
|
||||
lats = seg['lats']
|
||||
|
||||
# Binary search to find the segment indices O(log n)
|
||||
idx = bisect.bisect_right(lons, lon)
|
||||
|
||||
if idx == 0:
|
||||
boundary_lat = lats[0]
|
||||
elif idx == len(lons):
|
||||
boundary_lat = lats[-1]
|
||||
else:
|
||||
# Simple Linear Interpolation (no square roots/Pythagoras)
|
||||
# lat = y1 + (y2 - y1) * (x - x1) / (x2 - x1)
|
||||
x1, x2 = lons[idx-1], lons[idx]
|
||||
y1, y2 = lats[idx-1], lats[idx]
|
||||
|
||||
# Basic ratio calculation
|
||||
t = (lon - x1) / (x2 - x1)
|
||||
boundary_lat = y1 + t * (y2 - y1)
|
||||
|
||||
# Once we find the boundary at this lon, we compare
|
||||
# Note: If the line doubles back, this logic uses the first segment found.
|
||||
break
|
||||
|
||||
if boundary_lat is None:
|
||||
return False, "None"
|
||||
|
||||
# Compare input lat to boundary lat
|
||||
# North of boundary = 1626, South = 1623
|
||||
area = "1626" if lat >= boundary_lat else "1623"
|
||||
return True, area
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<gpx version="1.1" creator="Python Script" xmlns="http://www.topografix.com/GPX/1/1"><wpt lat="47.690985950000005" lon="13.72626763"><name>1626_North_Test</name><desc>Expected Area: 1626</desc></wpt><wpt lat="47.68998595" lon="13.72526763"><name>1623_South_Test</name><desc>Expected Area: 1623</desc></wpt><wpt lat="47.68582296" lon="13.73562238"><name>1626_North_Test</name><desc>Expected Area: 1626</desc></wpt><wpt lat="47.68482296" lon="13.73462238"><name>1623_South_Test</name><desc>Expected Area: 1623</desc></wpt><wpt lat="47.67491976" lon="13.770484790000001"><name>1626_North_Test</name><desc>Expected Area: 1626</desc></wpt><wpt lat="47.67391976" lon="13.76948479"><name>1623_South_Test</name><desc>Expected Area: 1623</desc></wpt><wpt lat="47.68959077" lon="13.79141566"><name>1626_North_Test</name><desc>Expected Area: 1626</desc></wpt><wpt lat="47.68859077" lon="13.790415659999999"><name>1623_South_Test</name><desc>Expected Area: 1623</desc></wpt><wpt lat="47.70586617" lon="13.82452883"><name>1626_North_Test</name><desc>Expected Area: 1626</desc></wpt><wpt lat="47.704866169999995" lon="13.823528829999999"><name>1623_South_Test</name><desc>Expected Area: 1623</desc></wpt><wpt lat="47.70052466" lon="13.859815350000002"><name>1626_North_Test</name><desc>Expected Area: 1626</desc></wpt><wpt lat="47.699524659999994" lon="13.85881535"><name>1623_South_Test</name><desc>Expected Area: 1623</desc></wpt></gpx>
|
||||
Reference in New Issue
Block a user