Skip to content

Commit

Permalink
Added support for loading CSV with WGS84
Browse files Browse the repository at this point in the history
Signed-off-by: Michał Pełka <[email protected]>
  • Loading branch information
michalpelka committed May 24, 2024
1 parent dc7376d commit c315f65
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 119 deletions.
1 change: 1 addition & 0 deletions Gems/RobotecSplineTools/Code/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AzToolsFramework
$<TARGET_OBJECTS:Gem::${gem_name}.Private.Object>
Gem::AtomLyIntegration_CommonFeatures.Static
Gem::ROS2.Editor.Static
)

ly_add_target(
Expand Down
273 changes: 156 additions & 117 deletions Gems/RobotecSplineTools/Code/Source/Tools/SplineToolsEditorComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,58 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <LmbrCentral/Shape/SplineComponentBus.h>
#include <csv/csv.hpp>
#include <QMessageBox>
#include <QFileDialog>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <ROS2/Georeference/GeoreferenceStructures.h>
#include <ROS2/Georeference/GeoreferenceBus.h>

namespace SplineTools
{
namespace SplineTools {

void SplineToolsEditorComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
void SplineToolsEditorComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType &required) {
required.push_back(AZ_CRC_CE("TransformService"));
required.push_back(AZ_CRC_CE("SplineService"));
}

void SplineToolsEditorComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
void SplineToolsEditorComponent::Reflect(AZ::ReflectContext *context) {
AZ::SerializeContext *serializeContext = azrtti_cast<AZ::SerializeContext *>(context);
if (serializeContext) {
serializeContext->Class<SplineToolsEditorComponent, AzToolsFramework::Components::EditorComponentBase>()
->Version(2)
->Field("CsvAssetId", &SplineToolsEditorComponent::m_csvAssetId)
->Field("IsLocalCoordinates", &SplineToolsEditorComponent::m_isLocalCoordinates);

AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<SplineToolsEditorComponent>("SplineToolsEditorComponent", "SplineToolsEditorComponent")
->ClassElement(AZ::Edit::ClassElements::EditorData, "SplineToolsEditorComponent")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
->Attribute(AZ::Edit::Attributes::Category, "RobotecTools")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(
AZ::Edit::UIHandlers::CheckBox,
&SplineToolsEditorComponent::m_isLocalCoordinates,
"Local coordinates",
"Local coordinates")
->DataElement(AZ::Edit::UIHandlers::Default, &SplineToolsEditorComponent::m_csvAssetId, "CSV Asset", "CSV asset")
->Attribute(AZ::Edit::Attributes::SourceAssetFilterPattern, "*.csv")
->UIElement(AZ::Edit::UIHandlers::Button, "Reload spline", "Reload spline")
->Attribute(AZ::Edit::Attributes::ButtonText, "Load")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &SplineToolsEditorComponent::ReloadCSVAsset)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->UIElement(AZ::Edit::UIHandlers::Button, "Save spline", "Save spline")
->Attribute(AZ::Edit::Attributes::ButtonText, "Save")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &SplineToolsEditorComponent::SaveCsvAsset)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "");
->Version(2)
->Field("IsLocalCoordinates", &SplineToolsEditorComponent::m_isLocalCoordinates);

AZ::EditContext *editContext = serializeContext->GetEditContext();
if (editContext) {
editContext->Class<SplineToolsEditorComponent>("SplineToolsEditorComponent",
"SplineToolsEditorComponent")
->ClassElement(AZ::Edit::ClassElements::EditorData, "SplineToolsEditorComponent")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
->Attribute(AZ::Edit::Attributes::Category, "RobotecTools")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(
AZ::Edit::UIHandlers::CheckBox,
&SplineToolsEditorComponent::m_isLocalCoordinates,
"Local coordinates",
"Local coordinates")
->UIElement(AZ::Edit::UIHandlers::Button, "Load spline", "Load spline")
->Attribute(AZ::Edit::Attributes::ButtonText, "Load")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &SplineToolsEditorComponent::ReloadCSVAsset)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
->UIElement(AZ::Edit::UIHandlers::Button, "Save spline", "Save spline")
->Attribute(AZ::Edit::Attributes::ButtonText, "Save")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &SplineToolsEditorComponent::SaveCsvAsset)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "");
}
}
}

void SplineToolsEditorComponent::Activate()
{
void SplineToolsEditorComponent::Activate() {
}

void SplineToolsEditorComponent::Deactivate()
{
void SplineToolsEditorComponent::Deactivate() {
}

void SplineToolsEditorComponent::BuildGameEntity([[maybe_unused]] AZ::Entity* gameEntity)
{
void SplineToolsEditorComponent::BuildGameEntity([[maybe_unused]] AZ::Entity *gameEntity) {
ReloadCSVAsset();
}

Expand All @@ -74,112 +69,129 @@ namespace SplineTools
LmbrCentral::SplineComponentRequestBus::EventResult(
splinePtr, GetEntityId(), &LmbrCentral::SplineComponentRequestBus::Events::GetSpline);
auto vertices = splinePtr->GetVertices();
using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
AZ::Data::AssetInfo sourceAssetInfo;
bool ok{ false };
AZStd::string watchFolder;
AZStd::vector<AZ::Data::AssetInfo> productsAssetInfo;

AssetSysReqBus::BroadcastResult(
ok, &AssetSysReqBus::Events::GetSourceInfoBySourceUUID, m_csvAssetId.m_guid, sourceAssetInfo, watchFolder);
if (!ok)
{
AZ_Error("SplineToolsEditorComponent", false, "Failed to get source info for referenced CSV asset. Saving aborted");


QString fileName = QFileDialog::getSaveFileName(
AzToolsFramework::GetActiveWindow(),
"Open CSV File",
"",
"CSV Files (*.csv)"
);

if (fileName.isEmpty()) {
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), "Error",
"Please specify file", QMessageBox::Ok);
return;
}
const AZ::IO::Path sourcePath = AZ::IO::Path(watchFolder) / AZ::IO::Path(sourceAssetInfo.m_relativePath);


const AZ::IO::Path sourcePath = AZ::IO::Path(fileName.toUtf8().constData());

if (!m_isLocalCoordinates)
{
auto worldTm{ AZ::Transform::Identity() };

AZ::TransformBus::EventResult(worldTm, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
AZStd::transform(
vertices.begin(),
vertices.end(),
vertices.begin(),
[worldTm](auto point)
{
return worldTm.TransformPoint(point);
});
vertices.begin(),
vertices.end(),
vertices.begin(),
[worldTm](auto point) {
return worldTm.TransformPoint(point);
});
}
AZ_Printf("SplineToolsEditorComponent", "Save CSV asset to %s", sourceAssetInfo.m_relativePath.c_str());
AZ_Printf("SplineToolsEditorComponent", "Save CSV asset to %s", sourcePath.c_str());
SavePointsToCsv(sourcePath.c_str(), vertices);
}

void SplineToolsEditorComponent::ReloadCSVAsset()
{
using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
AZ::Data::AssetInfo sourceAssetInfo;
bool ok{ false };
AZStd::string watchFolder;
AZStd::vector<AZ::Data::AssetInfo> productsAssetInfo;
void SplineToolsEditorComponent::ReloadCSVAsset() {

AssetSysReqBus::BroadcastResult(
ok, &AssetSysReqBus::Events::GetSourceInfoBySourceUUID, m_csvAssetId.m_guid, sourceAssetInfo, watchFolder);
const AZ::IO::Path sourcePath = AZ::IO::Path(watchFolder) / AZ::IO::Path(sourceAssetInfo.m_relativePath);
QString fileName = QFileDialog::getOpenFileName(
AzToolsFramework::GetActiveWindow(),
"Open CSV File",
"",
"CSV Files (*.csv)"
);

if (fileName.isEmpty()) {
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), "Error",
"Please specify file", QMessageBox::Ok);
return;
}
AZStd::string sourcePath = AZStd::string(fileName.toUtf8().constData());
AZ_Printf("SplineToolsEditorComponent", "Reload csv asset from %s", sourcePath.c_str());

auto points = GetSplinePointsFromCsv(sourcePath.c_str());

if (points.empty())
{
if (points.empty()) {
AZ_Error("SplineToolsEditorComponent", false, "No points found in CSV file");
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), "Error",
"No points found in CSV file", QMessageBox::Ok);
return;
}


AZ::Transform worldInvTm(AZ::Transform::Identity());
if (!m_isLocalCoordinates)
{
if (m_isLocalCoordinates && m_isLatLonAlt) {
AZ_Error("SplineToolsEditorComponent", false, "Cannot use lat, lon, alt coordinates in local coordinates");
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), "Error",
"Cannot use lat, lon, alt coordinates in local coordinates", QMessageBox::Ok);
return;
}

if (!m_isLocalCoordinates) {
AZ::TransformBus::EventResult(worldInvTm, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
worldInvTm.Invert();
}

LmbrCentral::SplineComponentRequestBus::Event(GetEntityId(), &LmbrCentral::SplineComponentRequestBus::Events::ClearVertices);

LmbrCentral::SplineComponentRequestBus::Event(GetEntityId(),
&LmbrCentral::SplineComponentRequestBus::Events::ClearVertices);
AZ_Printf("SplineToolsEditorComponent", "CSV file has %d points", points.size());

AZStd::transform(
points.begin(),
points.end(),
points.begin(),
[&worldInvTm](AZ::Vector3& p)
{
return worldInvTm.TransformPoint(p);
});

LmbrCentral::SplineComponentRequestBus::Event(GetEntityId(), &LmbrCentral::SplineComponentRequestBus::Events::SetVertices, points);
points.begin(),
points.end(),
points.begin(),
[&worldInvTm](AZ::Vector3 &p) {
return worldInvTm.TransformPoint(p);
});

LmbrCentral::SplineComponentRequestBus::Event(GetEntityId(),
&LmbrCentral::SplineComponentRequestBus::Events::SetVertices,
points);


}

void SplineToolsEditorComponent::SavePointsToCsv(const AZStd::string& csvFilePath, const AZStd::vector<AZ::Vector3>& vertices)
{
try
{
void SplineToolsEditorComponent::SavePointsToCsv(const AZStd::string &csvFilePath,
const AZStd::vector<AZ::Vector3> &vertices) {
try {
auto fileStream = std::ofstream(csvFilePath.c_str());

if (!fileStream.is_open())
{
AZ_Error("SplineToolsEditorComponent", false, "Could not open file for writing - (%s).", csvFilePath.c_str());
if (!fileStream.is_open()) {
AZ_Error("SplineToolsEditorComponent", false, "Could not open file for writing - (%s).",
csvFilePath.c_str());
return;
}
auto writer = csv::make_csv_writer(fileStream);
writer << std::make_tuple("x", "y", "z");
for (auto& vertex : vertices)
{
for (auto &vertex: vertices) {
writer << std::make_tuple(vertex.GetX(), vertex.GetY(), vertex.GetZ());
}
writer.flush();
AZ_Info("SplineToolsEditorComponent", "Saved %zu points to CSV file", vertices.size());
} catch (std::runtime_error& exception)
{
} catch (std::runtime_error &exception) {
AZ_Error("SplineToolsEditorComponent", false, "Error saving CSV file: %s", exception.what());
}
}

AZStd::vector<AZ::Vector3> SplineToolsEditorComponent::GetSplinePointsFromCsv(const AZStd::string& csvFilePath)
{
try
{
AZStd::vector<AZ::Vector3> SplineToolsEditorComponent::GetSplinePointsFromCsv(const AZStd::string &csvFilePath) {
try {
AZStd::vector<AZ::Vector3> ret;

csv::CSVReader reader(csvFilePath.c_str());
Expand All @@ -189,30 +201,57 @@ namespace SplineTools
const int index_Y = reader.index_of("y");
const int index_Z = reader.index_of("z");

const bool isCoordinateCorrect = !(index_X < 0 || index_Y < 0);
if (!isCoordinateCorrect)
{
AZ_Error("SplineToolsEditorComponent", false, "CSV file must have columns named x, y");
const int index_Lat = reader.index_of("lat");
const int index_Lon = reader.index_of("lon");
const int index_Alt = reader.index_of("alt");

const bool isCoordinateXY = !(index_X < 0 || index_Y < 0);
const bool isCoordinateLatLon = !(index_Lat < 0 || index_Lon < 0 || index_Alt < 0);
const bool isCoordinateCorrect = isCoordinateXY || isCoordinateLatLon;

if (!isCoordinateCorrect) {
AZ_Error("SplineToolsEditorComponent", false, "CSV file must have columns named x, y or lat, lon");
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), "Error",
"CSV file must have columns named x, y or lat, lon", QMessageBox::Ok);
return {};
}

for (csv::CSVRow& row : reader)
{
AZ::Vector3 point = AZ::Vector3(row[index_X].get<float>(), row[index_Y].get<float>(), 0);
if (isCoordinateXY) {
m_isLatLonAlt = false;
for (csv::CSVRow &row: reader) {
AZ::Vector3 point = AZ::Vector3(row[index_X].get<float>(), row[index_Y].get<float>(), 0);

// handle Z column
if (index_Z > 0)
{
point.SetZ(row[index_Z].get<float>());
}
// handle Z column
if (index_Z > 0) {
point.SetZ(row[index_Z].get<float>());
}

ret.emplace_back(AZStd::move(point));
ret.emplace_back(AZStd::move(point));
}
} else if (isCoordinateLatLon) {
m_isLatLonAlt = true;
for (csv::CSVRow &row: reader) {

ROS2::WGS::WGS84Coordinate coordinate;
coordinate.m_latitude = row[index_Lat].get<double>();
coordinate.m_longitude = row[index_Lon].get<double>();
coordinate.m_altitude = row[index_Alt].get<float>();
AZ::Vector3 coordinateInLevel = AZ::Vector3(-1);
ROS2::GeoreferenceRequestsBus::BroadcastResult(
coordinateInLevel, &ROS2::GeoreferenceRequests::ConvertFromWSG84ToLevel, coordinate);

ret.emplace_back(AZStd::move(coordinateInLevel));
}
}


return ret;
} catch (std::runtime_error& exception)
{
AZ_Error("SplineToolsEditorComponent", false, "Error parsing CSV file: %s", exception.what());
} catch (std::runtime_error &exception) {
AZStd::string error = AZStd::string::format("Error parsing CSV file: %s", exception.what());
AZ_Error("SplineToolsEditorComponent", false, error.c_str());
QMessageBox::warning(
AzToolsFramework::GetActiveWindow(), "Error", error.c_str(), QMessageBox::Ok);
}
return {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ namespace SplineTools
void BuildGameEntity(AZ::Entity* gameEntity) override;

private:
AZ::Data::AssetId m_csvAssetId; //!< Asset ID of the CSV
bool m_isLocalCoordinates = true;
bool m_isLatLonAlt = false;

void ReloadCSVAsset();
AZStd::vector<AZ::Vector3> GetSplinePointsFromCsv(const AZStd::string& csvFilePath);
Expand Down
2 changes: 1 addition & 1 deletion Gems/RobotecSplineTools/gem.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"icon_path": "preview.png",
"requirements": "No requirements",
"documentation_url": "",
"dependencies": [],
"dependencies": ["ROS2"],
"repo_uri": "",
"compatible_engines": [],
"engine_api_dependencies": [],
Expand Down

0 comments on commit c315f65

Please sign in to comment.