mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2026-05-10 16:07:23 +01:00
more area location tests
This commit is contained in:
+73
-3
@@ -75,6 +75,76 @@ def run_12_point_test(which_area_func):
|
||||
|
||||
print(f"\nSummary: {passed}/12 points passed.")
|
||||
|
||||
# Uncomment to run:
|
||||
run_limit_tests(which_area)
|
||||
run_12_point_test(which_area)
|
||||
|
||||
import random
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
from position_utils import which_area
|
||||
|
||||
def generate_performance_test(num_points=200):
|
||||
# Border limits derived from GPX data
|
||||
west_lon = 13.72476763 # Point 1626-22
|
||||
east_lon = 13.86031535 # Point 1626-256
|
||||
|
||||
# Calculate the span to create a square N/S range
|
||||
span = east_lon - west_lon
|
||||
|
||||
# Midpoint of the border (approximate center for the test square)
|
||||
mid_lat = 47.69
|
||||
min_lat = mid_lat - (span / 2)
|
||||
max_lat = mid_lat + (span / 2)
|
||||
|
||||
test_points = []
|
||||
for _ in range(num_points):
|
||||
lat = random.uniform(min_lat, max_lat)
|
||||
lon = random.uniform(west_lon, east_lon)
|
||||
test_points.append((lat, lon))
|
||||
|
||||
return test_points
|
||||
|
||||
def export_random_points_gpx(points_with_results, filename="random_test_points.gpx"):
|
||||
root = ET.Element("gpx", version="1.1", creator="PerformanceTester",
|
||||
xmlns="http://www.topografix.com/GPX/1/1")
|
||||
|
||||
for lat, lon, area, valid in points_with_results:
|
||||
wpt = ET.SubElement(root, "wpt", lat=f"{lat:.8f}", lon=f"{lon:.8f}")
|
||||
name = ET.SubElement(wpt, "name")
|
||||
name.text = f"{area}"
|
||||
desc = ET.SubElement(wpt, "desc")
|
||||
desc.text = f"Valid: {valid}"
|
||||
|
||||
tree = ET.ElementTree(root)
|
||||
tree.write(filename, encoding="utf-8", xml_declaration=True)
|
||||
print(f"Random points exported to {filename}")
|
||||
|
||||
def run_performance_test():
|
||||
# 1. Generate coordinates
|
||||
coords = generate_performance_test(200)
|
||||
results = []
|
||||
|
||||
# 2. Start Timing
|
||||
start_time = time.perf_counter()
|
||||
|
||||
for lat, lon in coords:
|
||||
valid, area = which_area(lat, lon)
|
||||
results.append((lat, lon, area, valid))
|
||||
|
||||
end_time = time.perf_counter()
|
||||
|
||||
# 3. Output results
|
||||
total_time_ms = (end_time - start_time) * 1000
|
||||
avg_time_us = (total_time_ms * 1000) / len(coords)
|
||||
|
||||
print(f"--- Performance Results ---")
|
||||
print(f"Total time for {len(coords)} points: {total_time_ms:.4f} ms")
|
||||
print(f"Average time per lookup: {avg_time_us:.2f} microseconds")
|
||||
|
||||
# 4. Export for visualization
|
||||
export_random_points_gpx(results)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_limit_tests(which_area)
|
||||
run_12_point_test(which_area)
|
||||
run_performance_test()
|
||||
|
||||
|
||||
|
||||
+16
-18
@@ -6,7 +6,7 @@ def load_and_clean_gpx(filename):
|
||||
if not Path(filename).exists:
|
||||
print("No file")
|
||||
else:
|
||||
print(f"Loading {filename}")
|
||||
print(f"Loading '{filename}'")
|
||||
# Parse GPX
|
||||
tree = ET.parse(filename)
|
||||
root = tree.getroot()
|
||||
@@ -64,7 +64,7 @@ def save_cleaned_gpx(points, output_filename="cleaned_border.gpx"):
|
||||
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}")
|
||||
print(f"Cleaned track exported to '{output_filename}'")
|
||||
|
||||
|
||||
def split_into_monotonic_segments(points):
|
||||
@@ -94,18 +94,11 @@ def split_into_monotonic_segments(points):
|
||||
current_segment.append(p_curr)
|
||||
|
||||
segments.append(current_segment)
|
||||
print(len(segments))
|
||||
print(f"{len(segments)} segments in border.")
|
||||
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)
|
||||
@@ -116,6 +109,7 @@ def generate_boundary_segments():
|
||||
|
||||
# Prepare segments for binary search
|
||||
# We store each segment as (sorted_lons, corresponding_lats)
|
||||
PREPARED_SEGMENTS = []
|
||||
for seg in mono_segments:
|
||||
lons = [p[0] for p in seg]
|
||||
lats = [p[1] for p in seg]
|
||||
@@ -132,23 +126,27 @@ def generate_boundary_segments():
|
||||
'min_lon': min(lons),
|
||||
'max_lon': max(lons)
|
||||
})
|
||||
return MIN_LON, MAX_LON
|
||||
return PREPARED_SEGMENTS, MIN_LON, MAX_LON
|
||||
|
||||
def which_area(lat, lon):
|
||||
global PREPARED_SEGMENTS, MIN_LON, MAX_LON
|
||||
# Initialize state on the function object itself if it doesn't exist
|
||||
# Yes, this is an attrubte *on a function*, an unusual python capbility
|
||||
if not hasattr(which_area, "data"):
|
||||
prepared_segments, min_l, max_l = generate_boundary_segments()
|
||||
which_area.data = {
|
||||
"prepared_segments": prepared_segments,
|
||||
"min_lon": min_l,
|
||||
"max_lon": max_l
|
||||
}
|
||||
|
||||
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:
|
||||
if lon < which_area.data["min_lon"] or lon > which_area.data["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:
|
||||
for seg in which_area.data["prepared_segments"]:
|
||||
# Check if lon is within this monotonic segment
|
||||
if seg['min_lon'] <= lon <= seg['max_lon']:
|
||||
lons = seg['lons']
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user