opencoverage 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opencoverage/__init__.py +26 -0
- opencoverage/cli.py +60 -0
- opencoverage/geometry/__init__.py +6 -0
- opencoverage/geometry/map.py +117 -0
- opencoverage/geometry/transforms.py +43 -0
- opencoverage/gpu/__init__.py +5 -0
- opencoverage/gpu/_backend.py +35 -0
- opencoverage/gpu/optimal_sweep.py +42 -0
- opencoverage/io/__init__.py +6 -0
- opencoverage/io/config.py +79 -0
- opencoverage/io/coords.py +72 -0
- opencoverage/io/kml.py +50 -0
- opencoverage/io/mission_planner.py +35 -0
- opencoverage/io/qgc.py +130 -0
- opencoverage/io/survey_input.py +21 -0
- opencoverage/mission_splitter.py +83 -0
- opencoverage/models.py +134 -0
- opencoverage/parameters.py +132 -0
- opencoverage/patterns/__init__.py +15 -0
- opencoverage/patterns/back_forth.py +57 -0
- opencoverage/patterns/base.py +74 -0
- opencoverage/patterns/following_wind.py +33 -0
- opencoverage/patterns/long_edge.py +35 -0
- opencoverage/patterns/optimal_sweep.py +38 -0
- opencoverage/planner.py +216 -0
- opencoverage-0.2.0.dist-info/METADATA +125 -0
- opencoverage-0.2.0.dist-info/RECORD +30 -0
- opencoverage-0.2.0.dist-info/WHEEL +4 -0
- opencoverage-0.2.0.dist-info/entry_points.txt +2 -0
- opencoverage-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Long-edge aligned coverage sweep pattern."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
from opencoverage.models import FlightMission
|
|
8
|
+
from opencoverage.patterns.back_forth import BackAndForthPattern
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LongEdgePattern(BackAndForthPattern):
|
|
12
|
+
"""Align sweep lines perpendicular to the polygon longest edge."""
|
|
13
|
+
|
|
14
|
+
def calculate(self) -> FlightMission:
|
|
15
|
+
rotation = self._long_edge_rotation()
|
|
16
|
+
waypoints = self.generate_rotated_pattern(rotation)
|
|
17
|
+
return self.build_mission(waypoints)
|
|
18
|
+
|
|
19
|
+
def _long_edge_rotation(self) -> float:
|
|
20
|
+
"""Return rotation angle that aligns the longest edge with the x-axis."""
|
|
21
|
+
coords = list(self.survey_map.polygon.exterior.coords[:-1])
|
|
22
|
+
origin = coords[0]
|
|
23
|
+
target = coords[1]
|
|
24
|
+
max_distance = 0.0
|
|
25
|
+
|
|
26
|
+
for index, start in enumerate(coords):
|
|
27
|
+
end = coords[(index + 1) % len(coords)]
|
|
28
|
+
distance = (end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2
|
|
29
|
+
if distance > max_distance:
|
|
30
|
+
max_distance = distance
|
|
31
|
+
origin = start
|
|
32
|
+
target = end
|
|
33
|
+
|
|
34
|
+
edge_angle = math.atan2(target[1] - origin[1], target[0] - origin[0])
|
|
35
|
+
return math.pi / 2.0 - edge_angle
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Optimal sweep direction coverage pattern."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from opencoverage.gpu.optimal_sweep import find_optimal_sweep_angle
|
|
6
|
+
from opencoverage.models import FlightMission
|
|
7
|
+
from opencoverage.patterns.back_forth import BackAndForthPattern
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OptimalSweepPattern(BackAndForthPattern):
|
|
11
|
+
"""
|
|
12
|
+
Find the sweep rotation that minimizes horizontal coverage width.
|
|
13
|
+
|
|
14
|
+
Uses a brute-force search over [0, pi). When ``gpu=True`` and CuPy is
|
|
15
|
+
installed, the search runs on the GPU.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
uav,
|
|
21
|
+
camera,
|
|
22
|
+
survey_map,
|
|
23
|
+
*,
|
|
24
|
+
gpu: bool = False,
|
|
25
|
+
n_angle_steps: int = 10_000,
|
|
26
|
+
) -> None:
|
|
27
|
+
super().__init__(uav, camera, survey_map)
|
|
28
|
+
self.gpu = gpu
|
|
29
|
+
self.n_angle_steps = n_angle_steps
|
|
30
|
+
|
|
31
|
+
def calculate(self) -> FlightMission:
|
|
32
|
+
angle = find_optimal_sweep_angle(
|
|
33
|
+
self.survey_map.vertices,
|
|
34
|
+
n_steps=self.n_angle_steps,
|
|
35
|
+
prefer_gpu=self.gpu,
|
|
36
|
+
)
|
|
37
|
+
waypoints = self.generate_rotated_pattern(angle)
|
|
38
|
+
return self.build_mission(waypoints)
|
opencoverage/planner.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""High-level mission planning API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from shapely.geometry import Polygon
|
|
8
|
+
|
|
9
|
+
from opencoverage.geometry.map import SurveyMap
|
|
10
|
+
from opencoverage.io.config import load_planner_config
|
|
11
|
+
from opencoverage.io.survey_input import read_survey_map
|
|
12
|
+
from opencoverage.mission_splitter import MissionSplitter
|
|
13
|
+
from opencoverage.models import (
|
|
14
|
+
FlightMission,
|
|
15
|
+
GeodeticCoordinate,
|
|
16
|
+
PinholeCamera,
|
|
17
|
+
SearchPatternType,
|
|
18
|
+
TargetPlanning,
|
|
19
|
+
UAV,
|
|
20
|
+
)
|
|
21
|
+
from opencoverage.patterns.back_forth import BackAndForthPattern
|
|
22
|
+
from opencoverage.patterns.base import SearchPattern
|
|
23
|
+
from opencoverage.patterns.following_wind import FollowingWindPattern
|
|
24
|
+
from opencoverage.patterns.long_edge import LongEdgePattern
|
|
25
|
+
from opencoverage.patterns.optimal_sweep import OptimalSweepPattern
|
|
26
|
+
|
|
27
|
+
PATTERN_ALIASES: dict[str, SearchPatternType] = {
|
|
28
|
+
"back_forth": SearchPatternType.BACK_AND_FORTH,
|
|
29
|
+
"back-and-forth": SearchPatternType.BACK_AND_FORTH,
|
|
30
|
+
"long_edge": SearchPatternType.LONG_EDGE,
|
|
31
|
+
"long-edge": SearchPatternType.LONG_EDGE,
|
|
32
|
+
"optimal_sweep": SearchPatternType.OPTIMAL_SWEEP,
|
|
33
|
+
"optimal-sweep": SearchPatternType.OPTIMAL_SWEEP,
|
|
34
|
+
"following_wind": SearchPatternType.FOLLOWING_WIND,
|
|
35
|
+
"following-wind": SearchPatternType.FOLLOWING_WIND,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _resolve_pattern_type(pattern: str | SearchPatternType) -> SearchPatternType:
|
|
40
|
+
if isinstance(pattern, SearchPatternType):
|
|
41
|
+
return pattern
|
|
42
|
+
normalized = pattern.lower().replace(" ", "_")
|
|
43
|
+
if normalized not in PATTERN_ALIASES:
|
|
44
|
+
raise ValueError(f"Unknown pattern: {pattern}")
|
|
45
|
+
return PATTERN_ALIASES[normalized]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _resolve_target(
|
|
49
|
+
target: str | TargetPlanning,
|
|
50
|
+
target_value: float | None,
|
|
51
|
+
config: dict | None,
|
|
52
|
+
) -> tuple[TargetPlanning, float]:
|
|
53
|
+
if config is not None:
|
|
54
|
+
planner = config["planner"]
|
|
55
|
+
resolved_target = planner["target_planning"]
|
|
56
|
+
if target_value is None:
|
|
57
|
+
if resolved_target == TargetPlanning.RESOLUTION:
|
|
58
|
+
target_value = planner["spatial_resolution_mm"]
|
|
59
|
+
elif resolved_target == TargetPlanning.VELOCITY:
|
|
60
|
+
target_value = planner["target_velocity"]
|
|
61
|
+
else:
|
|
62
|
+
target_value = config["uav"]["min_height"]
|
|
63
|
+
return resolved_target, float(target_value)
|
|
64
|
+
|
|
65
|
+
if isinstance(target, str):
|
|
66
|
+
target_map = {
|
|
67
|
+
"resolution": TargetPlanning.RESOLUTION,
|
|
68
|
+
"velocity": TargetPlanning.VELOCITY,
|
|
69
|
+
"altitude": TargetPlanning.ALTITUDE,
|
|
70
|
+
}
|
|
71
|
+
resolved_target = target_map[target.lower()]
|
|
72
|
+
else:
|
|
73
|
+
resolved_target = target
|
|
74
|
+
|
|
75
|
+
if target_value is None:
|
|
76
|
+
raise ValueError("target_value is required when no config file is provided")
|
|
77
|
+
|
|
78
|
+
return resolved_target, float(target_value)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _build_pattern(
|
|
82
|
+
pattern_type: SearchPatternType,
|
|
83
|
+
uav: UAV,
|
|
84
|
+
camera: PinholeCamera,
|
|
85
|
+
survey_map: SurveyMap,
|
|
86
|
+
*,
|
|
87
|
+
gpu: bool = False,
|
|
88
|
+
wind_direction_deg: float = 0.0,
|
|
89
|
+
n_angle_steps: int = 10_000,
|
|
90
|
+
) -> SearchPattern:
|
|
91
|
+
if pattern_type == SearchPatternType.BACK_AND_FORTH:
|
|
92
|
+
return BackAndForthPattern(uav, camera, survey_map)
|
|
93
|
+
if pattern_type == SearchPatternType.LONG_EDGE:
|
|
94
|
+
return LongEdgePattern(uav, camera, survey_map)
|
|
95
|
+
if pattern_type == SearchPatternType.OPTIMAL_SWEEP:
|
|
96
|
+
return OptimalSweepPattern(
|
|
97
|
+
uav,
|
|
98
|
+
camera,
|
|
99
|
+
survey_map,
|
|
100
|
+
gpu=gpu,
|
|
101
|
+
n_angle_steps=n_angle_steps,
|
|
102
|
+
)
|
|
103
|
+
if pattern_type == SearchPatternType.FOLLOWING_WIND:
|
|
104
|
+
return FollowingWindPattern(
|
|
105
|
+
uav,
|
|
106
|
+
camera,
|
|
107
|
+
survey_map,
|
|
108
|
+
wind_direction_deg=wind_direction_deg,
|
|
109
|
+
)
|
|
110
|
+
raise ValueError(f"Unsupported pattern: {pattern_type}")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _load_survey_map(
|
|
114
|
+
polygon: Polygon | str | Path,
|
|
115
|
+
*,
|
|
116
|
+
home: GeodeticCoordinate | None = None,
|
|
117
|
+
takeoff: GeodeticCoordinate | None = None,
|
|
118
|
+
reference: GeodeticCoordinate | None = None,
|
|
119
|
+
) -> SurveyMap:
|
|
120
|
+
if isinstance(polygon, (str, Path)):
|
|
121
|
+
survey_map = read_survey_map(polygon)
|
|
122
|
+
elif isinstance(polygon, Polygon):
|
|
123
|
+
survey_map = SurveyMap.from_polygon(polygon, reference=reference)
|
|
124
|
+
else:
|
|
125
|
+
raise TypeError("polygon must be a Shapely Polygon or a file path")
|
|
126
|
+
|
|
127
|
+
if reference is not None:
|
|
128
|
+
survey_map.reference = reference
|
|
129
|
+
if home is not None:
|
|
130
|
+
survey_map.home = home
|
|
131
|
+
if takeoff is not None:
|
|
132
|
+
survey_map.takeoff = takeoff
|
|
133
|
+
return survey_map
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def plan(
|
|
137
|
+
*,
|
|
138
|
+
polygon: Polygon | str | Path,
|
|
139
|
+
config: str | Path | None = None,
|
|
140
|
+
uav: UAV | None = None,
|
|
141
|
+
camera: PinholeCamera | None = None,
|
|
142
|
+
pattern: str | SearchPatternType = "back_forth",
|
|
143
|
+
lateral_overlap: float | None = None,
|
|
144
|
+
forward_overlap: float | None = None,
|
|
145
|
+
target: str | TargetPlanning = "velocity",
|
|
146
|
+
target_value: float | None = None,
|
|
147
|
+
home: GeodeticCoordinate | None = None,
|
|
148
|
+
takeoff: GeodeticCoordinate | None = None,
|
|
149
|
+
reference: GeodeticCoordinate | None = None,
|
|
150
|
+
split: bool = False,
|
|
151
|
+
gpu: bool = False,
|
|
152
|
+
wind_direction_deg: float = 0.0,
|
|
153
|
+
n_angle_steps: int = 10_000,
|
|
154
|
+
) -> FlightMission | list[FlightMission]:
|
|
155
|
+
"""
|
|
156
|
+
Plan a coverage mission for a survey polygon.
|
|
157
|
+
|
|
158
|
+
When ``split`` is True, the mission is divided into segments that respect
|
|
159
|
+
the UAV ``flight_time_s`` limit.
|
|
160
|
+
"""
|
|
161
|
+
parsed_config = load_planner_config(config) if config is not None else None
|
|
162
|
+
|
|
163
|
+
if parsed_config is not None:
|
|
164
|
+
uav = uav or parsed_config["uav_model"]
|
|
165
|
+
camera = camera or parsed_config["camera_model"]
|
|
166
|
+
lateral_overlap = (
|
|
167
|
+
lateral_overlap if lateral_overlap is not None else parsed_config["planner"]["lateral_overlap"]
|
|
168
|
+
)
|
|
169
|
+
forward_overlap = (
|
|
170
|
+
forward_overlap
|
|
171
|
+
if forward_overlap is not None
|
|
172
|
+
else parsed_config["planner"]["forward_overlap"]
|
|
173
|
+
)
|
|
174
|
+
if isinstance(pattern, str) and pattern in {"back_forth", "back-and-forth"}:
|
|
175
|
+
pattern = parsed_config["planner"]["search_pattern"]
|
|
176
|
+
wind_direction_deg = parsed_config["planner"].get("wind_direction_deg", wind_direction_deg)
|
|
177
|
+
else:
|
|
178
|
+
if uav is None or camera is None:
|
|
179
|
+
raise ValueError("uav and camera are required when no config file is provided")
|
|
180
|
+
if lateral_overlap is None or forward_overlap is None:
|
|
181
|
+
raise ValueError("overlap values are required when no config file is provided")
|
|
182
|
+
|
|
183
|
+
pattern_type = _resolve_pattern_type(pattern)
|
|
184
|
+
survey_map = _load_survey_map(
|
|
185
|
+
polygon,
|
|
186
|
+
home=home,
|
|
187
|
+
takeoff=takeoff,
|
|
188
|
+
reference=reference,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
resolved_target, resolved_target_value = _resolve_target(target, target_value, parsed_config)
|
|
192
|
+
|
|
193
|
+
search_pattern = _build_pattern(
|
|
194
|
+
pattern_type,
|
|
195
|
+
uav,
|
|
196
|
+
camera,
|
|
197
|
+
survey_map,
|
|
198
|
+
gpu=gpu,
|
|
199
|
+
wind_direction_deg=wind_direction_deg,
|
|
200
|
+
n_angle_steps=n_angle_steps,
|
|
201
|
+
)
|
|
202
|
+
search_pattern.set_search_parameters(
|
|
203
|
+
lateral_overlap=float(lateral_overlap),
|
|
204
|
+
forward_overlap=float(forward_overlap),
|
|
205
|
+
target_value=resolved_target_value,
|
|
206
|
+
target=resolved_target,
|
|
207
|
+
)
|
|
208
|
+
mission = search_pattern.calculate()
|
|
209
|
+
|
|
210
|
+
if parsed_config is not None:
|
|
211
|
+
mission.auto_takeoff = parsed_config["planner"]["auto_takeoff"]
|
|
212
|
+
|
|
213
|
+
should_split = bool(split)
|
|
214
|
+
if should_split:
|
|
215
|
+
return MissionSplitter(uav).split(mission)
|
|
216
|
+
return mission
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opencoverage
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Coverage path planning for UAV aerial surveying
|
|
5
|
+
Project-URL: Homepage, https://github.com/irvingvasquez/opencoverage
|
|
6
|
+
Project-URL: Repository, https://github.com/irvingvasquez/opencoverage
|
|
7
|
+
Project-URL: Documentation, https://github.com/irvingvasquez/opencoverage#readme
|
|
8
|
+
Project-URL: Issues, https://github.com/irvingvasquez/opencoverage/issues
|
|
9
|
+
Author-email: Juan Irving Vasquez <jvasquezg@ipn.mx>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: coverage-path-planning,drone,precision-agriculture,survey,uav
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: pyproj>=3.6
|
|
24
|
+
Requires-Dist: shapely>=2.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
28
|
+
Provides-Extra: gpu
|
|
29
|
+
Requires-Dist: cupy-cuda12x>=13.0; extra == 'gpu'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# OpenCoverage
|
|
33
|
+
|
|
34
|
+
Coverage path planning for UAV aerial surveying. OpenCoverage computes waypoint
|
|
35
|
+
flight paths that fully cover a terrain polygon, with support for camera overlap,
|
|
36
|
+
flight constraints, and multiple sweep patterns.
|
|
37
|
+
|
|
38
|
+
Python reimplementation of the original UAV Planning library, using Shapely
|
|
39
|
+
instead of CGAL. CPU execution is the default; optional GPU acceleration via CuPy
|
|
40
|
+
is available for the optimal sweep search.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- **Search patterns**: back-and-forth, long-edge, optimal sweep, following-wind
|
|
45
|
+
- **Planning targets**: spatial resolution, cruise velocity, or fixed altitude
|
|
46
|
+
- **Mission splitting** by UAV flight-time limits
|
|
47
|
+
- **Input formats**: KML polygons, Mission Planner `.poly` files, INI configuration
|
|
48
|
+
- **Output**: QGroundControl WPL 120 waypoint files
|
|
49
|
+
- **Optional GPU**: CuPy acceleration for optimal sweep angle search
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install -e ".[dev]"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Optional GPU support:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install -e ".[gpu]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from shapely.geometry import Polygon
|
|
67
|
+
|
|
68
|
+
import opencoverage as oc
|
|
69
|
+
|
|
70
|
+
polygon = Polygon([(0, 0), (100, 0), (100, 80), (0, 80)])
|
|
71
|
+
|
|
72
|
+
mission = oc.plan(
|
|
73
|
+
polygon=polygon,
|
|
74
|
+
uav=oc.UAV(min_height=70, max_height=500, survey_velocity=17),
|
|
75
|
+
camera=oc.PinholeCamera(
|
|
76
|
+
pixel_size_mm=0.0032,
|
|
77
|
+
focal_length_mm=8.43,
|
|
78
|
+
sensor_width_mm=6.55,
|
|
79
|
+
sensor_height_mm=4.92,
|
|
80
|
+
capture_rate_s=3,
|
|
81
|
+
),
|
|
82
|
+
pattern="optimal_sweep",
|
|
83
|
+
lateral_overlap=0.8,
|
|
84
|
+
forward_overlap=0.7,
|
|
85
|
+
target="velocity",
|
|
86
|
+
target_value=17.0,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
print(len(mission.path), "waypoints at", mission.flight_height, "m")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Command line
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
opencoverage examples/sample_field.kml config/quad_tetracam.ini mission.txt
|
|
96
|
+
opencoverage examples/sample_field.poly config/quad_tetracam.ini mission.txt --split
|
|
97
|
+
opencoverage examples/sample_field.kml config/quad_tetracam.ini mission.txt --gpu
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Patterns
|
|
101
|
+
|
|
102
|
+
| Pattern | Description |
|
|
103
|
+
|---------|-------------|
|
|
104
|
+
| `back_forth` | Standard boustrophedon sweep |
|
|
105
|
+
| `long_edge` | Sweep lines perpendicular to the longest polygon edge |
|
|
106
|
+
| `optimal_sweep` | Minimize horizontal sweep width over rotation angles |
|
|
107
|
+
| `following_wind` | Align sweeps with meteorological wind direction |
|
|
108
|
+
|
|
109
|
+
## Project layout
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
src/opencoverage/
|
|
113
|
+
├── models.py # UAV, camera, mission dataclasses
|
|
114
|
+
├── parameters.py # Survey parameter formulas
|
|
115
|
+
├── planner.py # High-level plan() API
|
|
116
|
+
├── mission_splitter.py # Flight-time mission splitting
|
|
117
|
+
├── geometry/ # Shapely map and transforms
|
|
118
|
+
├── patterns/ # Coverage search patterns
|
|
119
|
+
├── io/ # Config, KML, QGC, coordinate conversion
|
|
120
|
+
└── gpu/ # Optional CuPy kernels
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT License. Copyright (c) J. Irving Vasquez-Gomez.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
opencoverage/__init__.py,sha256=JXd5nL8Uitp0XQDkV16s22aK-_231O2e0XV4Z0BYFTI,534
|
|
2
|
+
opencoverage/cli.py,sha256=BRnYP-y3pgFU8JmlostHJk-R9bz1tIcVLfpEIs8K-Lc,1984
|
|
3
|
+
opencoverage/mission_splitter.py,sha256=1MaRZC0BtxlJZSoC2AxKrhETPfFQl4iB6JiVTzhb1Xw,2815
|
|
4
|
+
opencoverage/models.py,sha256=cgOk8zZqdwr9SsKGKp1i9Yj9agAlnuWSTUbidxYFIeg,3918
|
|
5
|
+
opencoverage/parameters.py,sha256=KUqQHSJdy3n-BiJ9OHRqbWT2u553uZN5jODYoH3simU,4459
|
|
6
|
+
opencoverage/planner.py,sha256=z_3nSwyCHENJUaDeAEa1AsOB4WCCYFWs1hELvK-GOm4,7423
|
|
7
|
+
opencoverage/geometry/__init__.py,sha256=MykmvDdZNt16vJdEMv106Ypk5MmEUhDqnDVd7A4C9rQ,250
|
|
8
|
+
opencoverage/geometry/map.py,sha256=ZmU4Oea96MOdmYAWv4ro1fS2zqDIgpSx8YBxImKN-X8,4415
|
|
9
|
+
opencoverage/geometry/transforms.py,sha256=tSy6dGkYNDJQ48-1Zp9hk_3Awl4rrgZPmJ1B-goEaXY,1268
|
|
10
|
+
opencoverage/gpu/__init__.py,sha256=brf3tSorembLz0m8gcQPlqSTQTslnx-sf5vDDxGskcw,164
|
|
11
|
+
opencoverage/gpu/_backend.py,sha256=BrM4u6bT812xBIoFlJjrk-ef9e-POZFNKvm1SJd-r3M,765
|
|
12
|
+
opencoverage/gpu/optimal_sweep.py,sha256=iUxfQM8IbX3a2eqp1i0V9D6ZG1J40bGfe0yNrRGhgVc,1124
|
|
13
|
+
opencoverage/io/__init__.py,sha256=SJY596r2dOc7LJgQ_B1eGGE9QxctgBBehygFAU22sPw,231
|
|
14
|
+
opencoverage/io/config.py,sha256=-yejas5meJ2llwJVoi48iGsgoztPMtLT-bsEx6nlyjA,3005
|
|
15
|
+
opencoverage/io/coords.py,sha256=XKVwxUBMnbMVuiKr6acqOp5MbA9VY9duEsAfOfHQsQ4,2031
|
|
16
|
+
opencoverage/io/kml.py,sha256=JwN_nyzQmJHPoy3s1HgZEVotQxK4vNIK9VvjxYCvKhw,1648
|
|
17
|
+
opencoverage/io/mission_planner.py,sha256=2p7GnUmjwGWjz1c-bzDrc4WUc-U6maH5DxsuzgPwQ4U,1218
|
|
18
|
+
opencoverage/io/qgc.py,sha256=spYHWjjLTDRWSLTUoMWQn1qgVWZxdj2NW08PA6GQtrQ,3232
|
|
19
|
+
opencoverage/io/survey_input.py,sha256=G_pJKmCfU9VN69V6WCVOsSVrBksNGAbEBfcB8piPqwk,660
|
|
20
|
+
opencoverage/patterns/__init__.py,sha256=z9BJVGU4Ow_emAdo3flsujNxON5iE212y3g6LaunUDI,505
|
|
21
|
+
opencoverage/patterns/back_forth.py,sha256=GCAkhrNfHV_I1Zx_jMOBTMgpQPjhLx8nOwkARtwi6Gw,2072
|
|
22
|
+
opencoverage/patterns/base.py,sha256=dOEHCl5stHJ-gMBPFks2lu-gQ-5crNQUxmz4unasSe4,2674
|
|
23
|
+
opencoverage/patterns/following_wind.py,sha256=3RzbHuaGw5NpWzxfZBlrxlOcPegn1I5jumV6JZNFyqI,907
|
|
24
|
+
opencoverage/patterns/long_edge.py,sha256=C9HQlX_d8f_B-2zkC3sdjD2nx4phokBM7IsQSBHS_Eo,1232
|
|
25
|
+
opencoverage/patterns/optimal_sweep.py,sha256=QlXmOaAwxgj_ifjlsIjCmE9INKtlSh7196K5fF0hD-s,1114
|
|
26
|
+
opencoverage-0.2.0.dist-info/METADATA,sha256=CIZ6Aw4wZfQmhvqhH3PeJ2kGq77X5WtAL2hUsdFlmsA,4115
|
|
27
|
+
opencoverage-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
28
|
+
opencoverage-0.2.0.dist-info/entry_points.txt,sha256=2MpVVSkV48CuLQ2dYOdN4utrtEmROnf3GgQtxXSBjbY,55
|
|
29
|
+
opencoverage-0.2.0.dist-info/licenses/LICENSE,sha256=WhZX_X8abVtWg47GfAfXZTXMRhThh4Hq48i17ru-Rls,1085
|
|
30
|
+
opencoverage-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018-2026 J. Irving Vasquez-Gomez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|