mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Improve CV waypointing UX.
This commit is contained in:
parent
ed7c8c11d9
commit
06dedf51aa
@ -97,21 +97,32 @@ class ControlPointJs(QObject):
|
|||||||
return []
|
return []
|
||||||
return self.theater.point_to_ll(self.control_point.target_position).as_list()
|
return self.theater.point_to_ll(self.control_point.target_position).as_list()
|
||||||
|
|
||||||
|
def destination_in_range(self, destination: Point) -> bool:
|
||||||
|
from qt_ui.widgets.map.QLiberationMap import MAX_SHIP_DISTANCE
|
||||||
|
|
||||||
|
move_distance = meters(
|
||||||
|
destination.distance_to_point(self.control_point.position)
|
||||||
|
)
|
||||||
|
return move_distance <= MAX_SHIP_DISTANCE
|
||||||
|
|
||||||
|
@Slot(list, result=bool)
|
||||||
|
def destinationInRange(self, destination: LeafletLatLon) -> bool:
|
||||||
|
return self.destination_in_range(self.theater.ll_to_point(LatLon(*destination)))
|
||||||
|
|
||||||
@Slot(list, result=str)
|
@Slot(list, result=str)
|
||||||
def setDestination(self, destination: LeafletLatLon) -> str:
|
def setDestination(self, destination: LeafletLatLon) -> str:
|
||||||
|
from qt_ui.widgets.map.QLiberationMap import MAX_SHIP_DISTANCE
|
||||||
|
|
||||||
if not self.control_point.moveable:
|
if not self.control_point.moveable:
|
||||||
return f"{self.control_point} is not mobile"
|
return f"{self.control_point} is not mobile"
|
||||||
if not self.control_point.captured:
|
if not self.control_point.captured:
|
||||||
return f"{self.control_point} is not owned by player"
|
return f"{self.control_point} is not owned by player"
|
||||||
point = self.theater.ll_to_point(LatLon(*destination))
|
|
||||||
from qt_ui.widgets.map.QLiberationMap import MAX_SHIP_DISTANCE
|
|
||||||
|
|
||||||
move_distance = meters(point.distance_to_point(self.control_point.position))
|
point = self.theater.ll_to_point(LatLon(*destination))
|
||||||
if move_distance > MAX_SHIP_DISTANCE:
|
if not self.destination_in_range(point):
|
||||||
return (
|
return (
|
||||||
f"Cannot move {self.control_point} more than "
|
f"Cannot move {self.control_point} more than "
|
||||||
f"{MAX_SHIP_DISTANCE.nautical_miles}nm. Attempted "
|
f"{MAX_SHIP_DISTANCE.nautical_miles}nm."
|
||||||
f"{move_distance.nautical_miles}nm"
|
|
||||||
)
|
)
|
||||||
self.control_point.target_position = point
|
self.control_point.target_position = point
|
||||||
self.destinationChanged.emit(destination)
|
self.destinationChanged.emit(destination)
|
||||||
|
|||||||
@ -15,8 +15,21 @@
|
|||||||
const Colors = Object.freeze({
|
const Colors = Object.freeze({
|
||||||
Blue: "#0084ff",
|
Blue: "#0084ff",
|
||||||
Red: "#c85050",
|
Red: "#c85050",
|
||||||
|
Green: "#80BA80",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function metersToNauticalMiles(meters) {
|
||||||
|
return meters * 0.000539957;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLatLng(latLng) {
|
||||||
|
const lat = latLng.lat.toFixed(2);
|
||||||
|
const lng = latLng.lng.toFixed(2);
|
||||||
|
const ns = lat >= 0 ? "N" : "S";
|
||||||
|
const ew = lng >= 0 ? "E" : "W";
|
||||||
|
return `${lat}°${ns} ${lng}°${ew}`;
|
||||||
|
}
|
||||||
|
|
||||||
const map = L.map("map").setView([0, 0], 3);
|
const map = L.map("map").setView([0, 0], 3);
|
||||||
L.control.scale({ maxWidth: 200 }).addTo(map);
|
L.control.scale({ maxWidth: 200 }).addTo(map);
|
||||||
|
|
||||||
@ -152,14 +165,35 @@ class ControlPoint {
|
|||||||
this.cp.setDestination([destination.lat, destination.lng]).then((err) => {
|
this.cp.setDestination([destination.lat, destination.lng]).then((err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(`Could not set control point destination: ${err}`);
|
console.log(`Could not set control point destination: ${err}`);
|
||||||
|
// Reset markers and paths on error. On success this happens when we get
|
||||||
|
// the destinationChanged signal from the backend.
|
||||||
|
this.onDestinationChanged();
|
||||||
}
|
}
|
||||||
// No need to update destination positions. The backend will emit an event
|
|
||||||
// that causes that if we've successfully changed the destination. If it
|
|
||||||
// was not successful, we've already reset the origin so no need for a
|
|
||||||
//change.
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDrag(destination) {
|
||||||
|
this.path.setLatLngs([this.cp.position, destination]);
|
||||||
|
this.path.addTo(controlPointsLayer);
|
||||||
|
const distance = metersToNauticalMiles(
|
||||||
|
destination.distanceTo(this.cp.position)
|
||||||
|
);
|
||||||
|
this.primaryMarker.unbindTooltip();
|
||||||
|
this.primaryMarker.bindTooltip(
|
||||||
|
`Move ${distance.toFixed(1)}nm to ${formatLatLng(destination)}`,
|
||||||
|
{
|
||||||
|
permanent: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.cp
|
||||||
|
.destinationInRange([destination.lat, destination.lng])
|
||||||
|
.then((inRange) => {
|
||||||
|
this.path.setStyle({
|
||||||
|
color: inRange ? Colors.Green : Colors.Red,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
detachTooltipsAndHandlers() {
|
detachTooltipsAndHandlers() {
|
||||||
this.primaryMarker.unbindTooltip();
|
this.primaryMarker.unbindTooltip();
|
||||||
this.primaryMarker.off("click");
|
this.primaryMarker.off("click");
|
||||||
@ -169,12 +203,13 @@ class ControlPoint {
|
|||||||
this.secondaryMarker.off("contextmenu");
|
this.secondaryMarker.off("contextmenu");
|
||||||
}
|
}
|
||||||
|
|
||||||
attachTooltipsAndHandlers() {
|
attachTooltipsAndHandlers(dragging = false) {
|
||||||
this.detachTooltipsAndHandlers();
|
this.detachTooltipsAndHandlers();
|
||||||
const zoom = map.getZoom();
|
const zoom = map.getZoom();
|
||||||
const locationMarker = this.hasDestination()
|
const locationMarker =
|
||||||
? this.secondaryMarker
|
this.hasDestination() || dragging
|
||||||
: this.primaryMarker;
|
? this.secondaryMarker
|
||||||
|
: this.primaryMarker;
|
||||||
const destinationMarker = this.hasDestination() ? this.primaryMarker : null;
|
const destinationMarker = this.hasDestination() ? this.primaryMarker : null;
|
||||||
locationMarker
|
locationMarker
|
||||||
.bindTooltip(`<h3 style="margin: 0;">${this.cp.name}</h3>`, {
|
.bindTooltip(`<h3 style="margin: 0;">${this.cp.name}</h3>`, {
|
||||||
@ -187,7 +222,15 @@ class ControlPoint {
|
|||||||
this.cp.showPackageDialog();
|
this.cp.showPackageDialog();
|
||||||
});
|
});
|
||||||
if (destinationMarker != null) {
|
if (destinationMarker != null) {
|
||||||
destinationMarker.bindTooltip(`${this.cp.name} destination`);
|
const origin = locationMarker.getLatLng();
|
||||||
|
const destination = destinationMarker.getLatLng();
|
||||||
|
const distance = metersToNauticalMiles(
|
||||||
|
destination.distanceTo(origin)
|
||||||
|
).toFixed(1);
|
||||||
|
const dest = formatLatLng(destination);
|
||||||
|
destinationMarker.bindTooltip(
|
||||||
|
`${this.cp.name} moving ${distance}nm to ${dest} next turn`
|
||||||
|
);
|
||||||
destinationMarker.on("contextmenu", () => this.cp.cancelTravel());
|
destinationMarker.on("contextmenu", () => this.cp.cancelTravel());
|
||||||
destinationMarker.addTo(map);
|
destinationMarker.addTo(map);
|
||||||
}
|
}
|
||||||
@ -206,6 +249,15 @@ class ControlPoint {
|
|||||||
draggable: this.cp.mobile,
|
draggable: this.cp.mobile,
|
||||||
autoPan: true,
|
autoPan: true,
|
||||||
})
|
})
|
||||||
|
.on("dragstart", () => {
|
||||||
|
this.secondaryMarker.addTo(controlPointsLayer);
|
||||||
|
this.attachTooltipsAndHandlers(true);
|
||||||
|
})
|
||||||
|
.on("drag", (event) => {
|
||||||
|
const marker = event.target;
|
||||||
|
const newPosition = marker.getLatLng();
|
||||||
|
this.onDrag(newPosition);
|
||||||
|
})
|
||||||
.on("dragend", (event) => {
|
.on("dragend", (event) => {
|
||||||
const marker = event.target;
|
const marker = event.target;
|
||||||
const newPosition = marker.getLatLng();
|
const newPosition = marker.getLatLng();
|
||||||
@ -224,7 +276,7 @@ class ControlPoint {
|
|||||||
makePath() {
|
makePath() {
|
||||||
const destination = this.hasDestination() ? this.cp.destination : [0, 0];
|
const destination = this.hasDestination() ? this.cp.destination : [0, 0];
|
||||||
return L.polyline([this.cp.position, destination], {
|
return L.polyline([this.cp.position, destination], {
|
||||||
color: "#80BA80",
|
color: Colors.Green,
|
||||||
weight: 1,
|
weight: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -235,6 +287,7 @@ class ControlPoint {
|
|||||||
this.secondaryMarker.addTo(controlPointsLayer);
|
this.secondaryMarker.addTo(controlPointsLayer);
|
||||||
this.path.setLatLngs([this.cp.position, this.cp.destination]);
|
this.path.setLatLngs([this.cp.position, this.cp.destination]);
|
||||||
this.path.addTo(controlPointsLayer);
|
this.path.addTo(controlPointsLayer);
|
||||||
|
this.path.setStyle({ color: Colors.Green });
|
||||||
} else {
|
} else {
|
||||||
this.hideDestination();
|
this.hideDestination();
|
||||||
this.primaryMarker.setLatLng(this.cp.position);
|
this.primaryMarker.setLatLng(this.cp.position);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user