Finish layer validation, including strong typing for different layer categories

This commit is contained in:
Dan Paulat 2023-10-27 08:40:24 -05:00
parent 8719d2d02a
commit f222b4f666
3 changed files with 243 additions and 49 deletions

View file

@ -44,8 +44,12 @@ static const std::string kDescriptionName_ {"description"};
static const std::string kMovableName_ {"movable"}; static const std::string kMovableName_ {"movable"};
static const std::string kDisplayedName_ {"displayed"}; static const std::string kDisplayedName_ {"displayed"};
typedef std:: typedef std::variant<std::monostate,
variant<std::monostate, types::Layer, awips::Phenomenon, std::string> types::DataLayer,
types::InformationLayer,
types::MapLayer,
awips::Phenomenon,
std::string>
LayerDescription; LayerDescription;
struct LayerInfo struct LayerInfo
@ -59,23 +63,24 @@ struct LayerInfo
typedef boost::container::stable_vector<LayerInfo> LayerVector; typedef boost::container::stable_vector<LayerInfo> LayerVector;
static const std::vector<LayerInfo> kDefaultLayers_ { static const std::vector<LayerInfo> kDefaultLayers_ {
{types::LayerType::Information, types::Layer::MapOverlay, false}, {types::LayerType::Information, types::InformationLayer::MapOverlay, false},
{types::LayerType::Information, types::Layer::ColorTable, false}, {types::LayerType::Information, types::InformationLayer::ColorTable, false},
{types::LayerType::Data, types::DataLayer::RadarRange, true},
{types::LayerType::Alert, awips::Phenomenon::Tornado, true}, {types::LayerType::Alert, awips::Phenomenon::Tornado, true},
{types::LayerType::Alert, awips::Phenomenon::SnowSquall, true}, {types::LayerType::Alert, awips::Phenomenon::SnowSquall, true},
{types::LayerType::Alert, awips::Phenomenon::SevereThunderstorm, true}, {types::LayerType::Alert, awips::Phenomenon::SevereThunderstorm, true},
{types::LayerType::Alert, awips::Phenomenon::FlashFlood, true}, {types::LayerType::Alert, awips::Phenomenon::FlashFlood, true},
{types::LayerType::Alert, awips::Phenomenon::Marine, true}, {types::LayerType::Alert, awips::Phenomenon::Marine, true},
{types::LayerType::Map, types::Layer::MapSymbology, false}, {types::LayerType::Map, types::MapLayer::MapSymbology, false},
{types::LayerType::Radar, std::monostate {}, true}, {types::LayerType::Radar, std::monostate {}, true},
{types::LayerType::Map, types::Layer::MapUnderlay, false}, {types::LayerType::Map, types::MapLayer::MapUnderlay, false},
}; };
static const std::vector<LayerInfo> kImmovableLayers_ { static const std::vector<LayerInfo> kImmovableLayers_ {
{types::LayerType::Information, types::Layer::MapOverlay, false}, {types::LayerType::Information, types::InformationLayer::MapOverlay, false},
{types::LayerType::Information, types::Layer::ColorTable, false}, {types::LayerType::Information, types::InformationLayer::ColorTable, false},
{types::LayerType::Map, types::Layer::MapSymbology, false}, {types::LayerType::Map, types::MapLayer::MapSymbology, false},
{types::LayerType::Map, types::Layer::MapUnderlay, false}, {types::LayerType::Map, types::MapLayer::MapUnderlay, false},
}; };
static const std::array<awips::Phenomenon, 5> kAlertPhenomena_ { static const std::array<awips::Phenomenon, 5> kAlertPhenomena_ {
@ -206,8 +211,40 @@ void LayerModel::Impl::ReadLayerSettings()
void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers) void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
{ {
// Validate layer properties
for (auto it = layers.begin(); it != layers.end();)
{
// If the layer is invalid, remove it
if (it->type_ == types::LayerType::Unknown ||
(std::holds_alternative<types::DataLayer>(it->description_) &&
std::get<types::DataLayer>(it->description_) ==
types::DataLayer::Unknown) ||
(std::holds_alternative<types::InformationLayer>(it->description_) &&
std::get<types::InformationLayer>(it->description_) ==
types::InformationLayer::Unknown) ||
(std::holds_alternative<types::MapLayer>(it->description_) &&
std::get<types::MapLayer>(it->description_) ==
types::MapLayer::Unknown) ||
(std::holds_alternative<awips::Phenomenon>(it->description_) &&
std::get<awips::Phenomenon>(it->description_) ==
awips::Phenomenon::Unknown))
{
// Erase the current layer and continue
it = layers.erase(it);
continue;
}
// Ensure layers are appropriately marked movable
it->movable_ = (it->type_ != types::LayerType::Information &&
it->type_ != types::LayerType::Map);
// Continue to the next layer
++it;
}
// Validate immovable layers // Validate immovable layers
std::vector<LayerVector::iterator> immovableIterators {}; std::vector<LayerVector::iterator> immovableIterators {};
LayerVector::iterator colorTableIterator {};
LayerVector::iterator mapSymbologyIterator {}; LayerVector::iterator mapSymbologyIterator {};
LayerVector::iterator mapUnderlayIterator {}; LayerVector::iterator mapUnderlayIterator {};
for (auto& immovableLayer : kImmovableLayers_) for (auto& immovableLayer : kImmovableLayers_)
@ -253,15 +290,27 @@ void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
} }
// Store positional iterators // Store positional iterators
if (it->type_ == types::LayerType::Map) if (it->type_ == types::LayerType::Information)
{ {
switch (std::get<types::Layer>(it->description_)) switch (std::get<types::InformationLayer>(it->description_))
{ {
case types::Layer::MapSymbology: case types::InformationLayer::ColorTable:
colorTableIterator = it;
break;
default:
break;
}
}
else if (it->type_ == types::LayerType::Map)
{
switch (std::get<types::MapLayer>(it->description_))
{
case types::MapLayer::MapSymbology:
mapSymbologyIterator = it; mapSymbologyIterator = it;
break; break;
case types::Layer::MapUnderlay: case types::MapLayer::MapUnderlay:
mapUnderlayIterator = it; mapUnderlayIterator = it;
break; break;
@ -274,7 +323,36 @@ void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
immovableIterators.push_back(it); immovableIterators.push_back(it);
} }
// Validate data layers
std::vector<LayerVector::iterator> dataIterators {};
for (const auto& dataLayer : types::DataLayerIterator())
{
// Find the data layer
auto it = std::find_if(layers.begin(),
layers.end(),
[&dataLayer](const LayerInfo& layer)
{
return layer.type_ == types::LayerType::Data &&
std::get<types::DataLayer>(
layer.description_) == dataLayer;
});
if (it == layers.end())
{
// If this is the first data layer, insert after the color table layer,
// otherwise, insert after the previous data layer
LayerVector::iterator insertPosition = dataIterators.empty() ?
colorTableIterator + 1 :
dataIterators.back() + 1;
it =
layers.insert(insertPosition, {types::LayerType::Data, dataLayer});
}
dataIterators.push_back(it);
}
// Validate alert layers // Validate alert layers
std::vector<LayerVector::iterator> alertIterators {};
for (auto& phenomenon : kAlertPhenomena_) for (auto& phenomenon : kAlertPhenomena_)
{ {
// Find the alert layer // Find the alert layer
@ -289,16 +367,24 @@ void LayerModel::Impl::ValidateLayerSettings(LayerVector& layers)
if (it == layers.end()) if (it == layers.end())
{ {
layers.insert(mapSymbologyIterator, // Insert before the map symbology layer
it = layers.insert(mapSymbologyIterator,
{types::LayerType::Alert, phenomenon}); {types::LayerType::Alert, phenomenon});
} }
alertIterators.push_back(it);
} }
// Ensure layers are appropriately marked movable // Validate the radar layer
for (auto& layer : layers) auto it = std::find_if(layers.begin(),
layers.end(),
[](const LayerInfo& layer)
{ return layer.type_ == types::LayerType::Radar; });
if (it == layers.end())
{ {
layer.movable_ = (layer.type_ != types::LayerType::Information && // Insert before the map underlay layer
layer.type_ != types::LayerType::Map); it = layers.insert(mapUnderlayIterator,
{types::LayerType::Radar, std::monostate {}});
} }
} }
@ -470,10 +556,23 @@ QVariant LayerModel::data(const QModelIndex& index, int role) const
return QString::fromStdString( return QString::fromStdString(
std::get<std::string>(layer.description_)); std::get<std::string>(layer.description_));
} }
else if (std::holds_alternative<types::Layer>(layer.description_)) else if (std::holds_alternative<types::DataLayer>(
layer.description_))
{ {
return QString::fromStdString(types::GetLayerName( return QString::fromStdString(types::GetDataLayerName(
std::get<types::Layer>(layer.description_))); std::get<types::DataLayer>(layer.description_)));
}
else if (std::holds_alternative<types::InformationLayer>(
layer.description_))
{
return QString::fromStdString(types::GetInformationLayerName(
std::get<types::InformationLayer>(layer.description_)));
}
else if (std::holds_alternative<types::MapLayer>(
layer.description_))
{
return QString::fromStdString(types::GetMapLayerName(
std::get<types::MapLayer>(layer.description_)));
} }
else if (std::holds_alternative<awips::Phenomenon>( else if (std::holds_alternative<awips::Phenomenon>(
layer.description_)) layer.description_))
@ -873,9 +972,10 @@ void LayerModel::Impl::AddPlacefile(const std::string& name)
layers_.end(), layers_.end(),
[](const LayerInfo& layerInfo) [](const LayerInfo& layerInfo)
{ {
return std::holds_alternative<types::Layer>(layerInfo.description_) && return std::holds_alternative<types::InformationLayer>(
std::get<types::Layer>(layerInfo.description_) == layerInfo.description_) &&
types::Layer::ColorTable; std::get<types::InformationLayer>(layerInfo.description_) ==
types::InformationLayer::ColorTable;
}); });
if (insertPosition != layers_.end()) if (insertPosition != layers_.end())
{ {
@ -899,10 +999,21 @@ void tag_invoke(boost::json::value_from_tag,
description = awips::GetPhenomenonCode( description = awips::GetPhenomenonCode(
std::get<awips::Phenomenon>(record.description_)); std::get<awips::Phenomenon>(record.description_));
} }
else if (std::holds_alternative<types::Layer>(record.description_)) else if (std::holds_alternative<types::DataLayer>(record.description_))
{
description = types::GetDataLayerName(
std::get<types::DataLayer>(record.description_));
}
else if (std::holds_alternative<types::InformationLayer>(
record.description_))
{
description = types::GetInformationLayerName(
std::get<types::InformationLayer>(record.description_));
}
else if (std::holds_alternative<types::MapLayer>(record.description_))
{ {
description = description =
types::GetLayerName(std::get<types::Layer>(record.description_)); types::GetMapLayerName(std::get<types::MapLayer>(record.description_));
} }
else if (std::holds_alternative<std::string>(record.description_)) else if (std::holds_alternative<std::string>(record.description_))
{ {
@ -940,10 +1051,17 @@ LayerInfo tag_invoke(boost::json::value_to_tag<LayerInfo>,
LayerDescription description {}; LayerDescription description {};
if (layerType == types::LayerType::Map || if (layerType == types::LayerType::Map)
layerType == types::LayerType::Information)
{ {
description = types::GetLayer(descriptionName); description = types::GetMapLayer(descriptionName);
}
else if (layerType == types::LayerType::Information)
{
description = types::GetInformationLayer(descriptionName);
}
else if (layerType == types::LayerType::Data)
{
description = types::GetDataLayer(descriptionName);
} }
else if (layerType == types::LayerType::Radar) else if (layerType == types::LayerType::Radar)
{ {

View file

@ -17,14 +17,21 @@ static const std::unordered_map<LayerType, std::string> layerTypeName_ {
{LayerType::Alert, "Alert"}, {LayerType::Alert, "Alert"},
{LayerType::Placefile, "Placefile"}, {LayerType::Placefile, "Placefile"},
{LayerType::Information, "Information"}, {LayerType::Information, "Information"},
{LayerType::Data, "Data"},
{LayerType::Unknown, "?"}}; {LayerType::Unknown, "?"}};
static const std::unordered_map<Layer, std::string> layerName_ { static const std::unordered_map<DataLayer, std::string> dataLayerName_ {
{Layer::MapOverlay, "Map Overlay"}, {DataLayer::RadarRange, "Radar Range"}, {DataLayer::Unknown, "?"}};
{Layer::ColorTable, "Color Table"},
{Layer::MapSymbology, "Map Symbology"}, static const std::unordered_map<InformationLayer, std::string>
{Layer::MapUnderlay, "Map Underlay"}, informationLayerName_ {{InformationLayer::MapOverlay, "Map Overlay"},
{Layer::Unknown, "?"}}; {InformationLayer::ColorTable, "Color Table"},
{InformationLayer::Unknown, "?"}};
static const std::unordered_map<MapLayer, std::string> mapLayerName_ {
{MapLayer::MapSymbology, "Map Symbology"},
{MapLayer::MapUnderlay, "Map Underlay"},
{MapLayer::Unknown, "?"}};
LayerType GetLayerType(const std::string& name) LayerType GetLayerType(const std::string& name)
{ {
@ -49,27 +56,73 @@ std::string GetLayerTypeName(LayerType layerType)
return layerTypeName_.at(layerType); return layerTypeName_.at(layerType);
} }
Layer GetLayer(const std::string& name) DataLayer GetDataLayer(const std::string& name)
{ {
auto result = auto result =
std::find_if(layerName_.cbegin(), std::find_if(dataLayerName_.cbegin(),
layerName_.cend(), dataLayerName_.cend(),
[&](const std::pair<Layer, std::string>& pair) -> bool [&](const std::pair<DataLayer, std::string>& pair) -> bool
{ return boost::iequals(pair.second, name); }); { return boost::iequals(pair.second, name); });
if (result != layerName_.cend()) if (result != dataLayerName_.cend())
{ {
return result->first; return result->first;
} }
else else
{ {
return Layer::Unknown; return DataLayer::Unknown;
} }
} }
std::string GetLayerName(Layer layer) std::string GetDataLayerName(DataLayer layer)
{ {
return layerName_.at(layer); return dataLayerName_.at(layer);
}
InformationLayer GetInformationLayer(const std::string& name)
{
auto result = std::find_if(
informationLayerName_.cbegin(),
informationLayerName_.cend(),
[&](const std::pair<InformationLayer, std::string>& pair) -> bool
{ return boost::iequals(pair.second, name); });
if (result != informationLayerName_.cend())
{
return result->first;
}
else
{
return InformationLayer::Unknown;
}
}
std::string GetInformationLayerName(InformationLayer layer)
{
return informationLayerName_.at(layer);
}
MapLayer GetMapLayer(const std::string& name)
{
auto result =
std::find_if(mapLayerName_.cbegin(),
mapLayerName_.cend(),
[&](const std::pair<MapLayer, std::string>& pair) -> bool
{ return boost::iequals(pair.second, name); });
if (result != mapLayerName_.cend())
{
return result->first;
}
else
{
return MapLayer::Unknown;
}
}
std::string GetMapLayerName(MapLayer layer)
{
return mapLayerName_.at(layer);
} }
} // namespace types } // namespace types

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <scwx/util/iterator.hpp>
#include <string> #include <string>
namespace scwx namespace scwx
@ -16,13 +18,28 @@ enum class LayerType
Alert, Alert,
Placefile, Placefile,
Information, Information,
Data,
Unknown Unknown
}; };
enum class Layer enum class DataLayer
{
RadarRange,
Unknown
};
typedef scwx::util::
Iterator<DataLayer, DataLayer::RadarRange, DataLayer::RadarRange>
DataLayerIterator;
enum class InformationLayer
{ {
MapOverlay, MapOverlay,
ColorTable, ColorTable,
Unknown
};
enum class MapLayer
{
MapSymbology, MapSymbology,
MapUnderlay, MapUnderlay,
Unknown Unknown
@ -31,8 +48,14 @@ enum class Layer
LayerType GetLayerType(const std::string& name); LayerType GetLayerType(const std::string& name);
std::string GetLayerTypeName(LayerType layerType); std::string GetLayerTypeName(LayerType layerType);
Layer GetLayer(const std::string& name); DataLayer GetDataLayer(const std::string& name);
std::string GetLayerName(Layer layer); std::string GetDataLayerName(DataLayer layer);
InformationLayer GetInformationLayer(const std::string& name);
std::string GetInformationLayerName(InformationLayer layer);
MapLayer GetMapLayer(const std::string& name);
std::string GetMapLayerName(MapLayer layer);
} // namespace types } // namespace types
} // namespace qt } // namespace qt