Merge pull request #2414 from OpenSpace/issue/2403

Change sky browser to use id's instead of indices to get the images
This commit is contained in:
Ylva Selling
2023-02-09 16:30:55 -05:00
committed by GitHub
10 changed files with 140 additions and 99 deletions

View File

@@ -3,7 +3,7 @@ asset.require("./static_server")
local guiCustomization = asset.require("customization/gui")
-- Select which commit hashes to use for the frontend and backend
local frontendHash = "5011a84942cee1567565a4be250501f4453f26a2"
local frontendHash = "644f308be66309c2089bf6f94929205b24d56a8f"
local dataProvider = "data.openspaceproject.com/files/webgui"
local frontend = asset.syncedResource({

View File

@@ -87,17 +87,17 @@ public:
std::string targetNodeId() const;
ScreenSpaceSkyBrowser* browser() const;
std::vector<int> selectedImages() const;
std::vector<std::string> selectedImages() const;
ghoul::Dictionary dataAsDictionary() const;
// WorldWide Telescope image handling
void setImageOrder(int i, int order);
void selectImage(const ImageData& image, int i);
void addImageLayerToWwt(const std::string& url, int i);
void removeSelectedImage(int i);
void setImageOrder(const std::string& imageUrl, int order);
void selectImage(const ImageData& image);
void addImageLayerToWwt(const std::string& imageUrl);
void removeSelectedImage(const std::string& imageUrl);
void loadImageCollection(const std::string& collection);
void setImageOpacity(int i, float opacity);
void setImageOpacity(const std::string& imageUrl, float opacity);
void hideChromeInterface();
private:

View File

@@ -31,6 +31,7 @@
#include <deque>
namespace openspace {
using SelectedImageDeque = std::deque<std::pair<std::string, double>>;
class WwtCommunicator : public Browser {
public:
@@ -40,12 +41,12 @@ public:
void update();
// WorldWide Telescope communication
void selectImage(const std::string& url, int i);
void addImageLayerToWwt(const std::string& url, int i);
void removeSelectedImage(int i);
void setImageOrder(int image, int order);
void selectImage(const std::string& imageUrl);
void addImageLayerToWwt(const std::string& imageUrl);
void removeSelectedImage(const std::string& imageUrl);
void setImageOrder(const std::string& imageUrl, int order);
void loadImageCollection(const std::string& collection);
void setImageOpacity(int i, float opacity);
void setImageOpacity(const std::string& imageUrl, float opacity);
void hideChromeInterface() const;
bool isImageCollectionLoaded() const;
@@ -54,7 +55,7 @@ public:
glm::ivec3 borderColor() const;
glm::dvec2 equatorialAim() const;
glm::dvec2 fieldsOfView() const;
std::vector<int> selectedImages() const;
std::vector<std::string> selectedImages() const;
std::vector<double> opacities() const;
double borderRadius() const;
@@ -70,7 +71,7 @@ public:
protected:
void setIdInBrowser(const std::string& id) const;
std::deque<std::pair<int, double>>::iterator findSelectedImage(int i);
SelectedImageDeque::iterator findSelectedImage(const std::string& id);
properties::DoubleProperty _verticalFov;
@@ -79,7 +80,7 @@ protected:
glm::dvec2 _equatorialAim = glm::dvec2(0.0);
double _targetRoll = 0.0;
bool _isImageCollectionLoaded = false;
std::deque<std::pair<int, double>> _selectedImages;
SelectedImageDeque _selectedImages;
private:
void sendMessageToWwt(const ghoul::Dictionary& msg) const;

View File

@@ -45,19 +45,21 @@ struct ImageData {
float fov = 0.f;
glm::dvec2 equatorialSpherical = glm::dvec2(0.0);
glm::dvec3 equatorialCartesian = glm::dvec3(0.0);
std::string identifier;
};
class WwtDataHandler {
public:
void loadImages(const std::string& root, const std::filesystem::path& directory);
int nLoadedImages() const;
const ImageData& image(int i) const;
std::optional<const ImageData> image(const std::string& imageUrl) const;
const std::map<std::string, ImageData>& images() const;
private:
void saveImagesFromXml(const tinyxml2::XMLElement* root, std::string collection);
// Images
std::vector<ImageData> _images;
std::map<std::string, ImageData> _images;
};
} // namespace openspace

View File

@@ -318,9 +318,12 @@ void SkyBrowserModule::setHoverCircle(SceneGraphNode* circle) {
disableHoverCircle();
}
void SkyBrowserModule::moveHoverCircle(int i, bool useScript) {
const ImageData& image = _dataHandler.image(i);
void SkyBrowserModule::moveHoverCircle(const std::string& imageUrl, bool useScript) {
std::optional<const ImageData> found = _dataHandler.image(imageUrl);
if (!found.has_value()) {
return;
}
const ImageData image = *found;
// Only move and show circle if the image has coordinates
if (!(_hoverCircle && image.hasCelestialCoords && _isCameraInSolarSystem)) {
return;

View File

@@ -76,7 +76,7 @@ public:
void addTargetBrowserPair(const std::string& targetId, const std::string& browserId);
// Hover circle
void moveHoverCircle(int i, bool useScript = true);
void moveHoverCircle(const std::string& imageUrl, bool useScript = true);
void disableHoverCircle(bool useScript = true);
// Image collection handling

View File

@@ -74,7 +74,7 @@ namespace {
* Takes an index to an image and selects that image in the currently
* selected sky browser.
*/
[[codegen::luawrap]] void selectImage(int imageIndex) {
[[codegen::luawrap]] void selectImage(std::string imageUrl) {
using namespace openspace;
// Load image
@@ -83,8 +83,17 @@ namespace {
if (module->isCameraInSolarSystem()) {
TargetBrowserPair* selected = module->pair(module->selectedBrowserId());
if (selected) {
const ImageData& image = module->wwtDataHandler().image(imageIndex);
std::optional<const ImageData> found = module->wwtDataHandler().image(
imageUrl
);
if (!found.has_value()) {
LINFO(fmt::format(
"No image with identifier {} was found in the collection.", imageUrl
));
return;
}
// Load image into browser
const ImageData& image = found.value();
std::string str = image.name;
// Check if character is ASCII - if it isn't, remove
str.erase(
@@ -97,7 +106,7 @@ namespace {
str.end()
);
LINFO("Loading image " + str);
selected->selectImage(image, imageIndex);
selected->selectImage(image);
bool isInView = skybrowser::isCoordinateInView(image.equatorialCartesian);
// If the coordinate is not in view, rotate camera
@@ -130,10 +139,10 @@ namespace {
/**
* Moves the hover circle to the coordinate specified by the image index.
*/
[[codegen::luawrap]] void moveCircleToHoverImage(int imageIndex) {
[[codegen::luawrap]] void moveCircleToHoverImage(std::string imageUrl) {
using namespace openspace;
global::moduleEngine->module<SkyBrowserModule>()->moveHoverCircle(imageIndex, false);
global::moduleEngine->module<SkyBrowserModule>()->moveHoverCircle(imageUrl, false);
}
/**
@@ -150,7 +159,7 @@ namespace {
* which it should have in the selected image list. The image is then changed to have this
* order.
*/
[[codegen::luawrap]] void setImageLayerOrder(std::string identifier, int imageIndex,
[[codegen::luawrap]] void setImageLayerOrder(std::string identifier, std::string imageUrl,
int imageOrder)
{
using namespace openspace;
@@ -158,7 +167,7 @@ namespace {
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
TargetBrowserPair* pair = module->pair(identifier);
if (pair) {
pair->setImageOrder(imageIndex, imageOrder);
pair->setImageOrder(imageUrl, imageOrder);
}
}
@@ -296,10 +305,7 @@ namespace {
// Create Lua table to send to the GUI
ghoul::Dictionary list;
for (int i = 0; i < module->nLoadedImages(); i++) {
const ImageData& img = module->wwtDataHandler().image(i);
for (auto const& [id, img] : module->wwtDataHandler().images()) {
// Push ("Key", value)
ghoul::Dictionary image;
image.setValue("name", img.name);
@@ -312,11 +318,9 @@ namespace {
image.setValue("hasCelestialCoords", img.hasCelestialCoords);
image.setValue("credits", img.credits);
image.setValue("creditsUrl", img.creditsUrl);
image.setValue("identifier", std::to_string(i));
image.setValue("identifier", img.identifier);
// Index for current ImageData
// Set table for the current ImageData
list.setValue(std::to_string(i + 1), image);
list.setValue(img.identifier, image);
}
return list;
@@ -409,15 +413,15 @@ namespace {
* Takes an identifier to a sky browser or sky target, an index to an image and a value
* for the opacity.
*/
[[codegen::luawrap]] void setOpacityOfImageLayer(std::string identifier, int imageIndex,
float opacity)
[[codegen::luawrap]] void setOpacityOfImageLayer(std::string identifier,
std::string imageUrl, float opacity)
{
using namespace openspace;
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
TargetBrowserPair* pair = module->pair(identifier);
if (pair) {
pair->setImageOpacity(imageIndex, opacity);
pair->setImageOpacity(imageUrl, opacity);
}
}
@@ -593,14 +597,14 @@ namespace {
* image from that sky browser.
*/
[[codegen::luawrap]] void removeSelectedImageInBrowser(std::string identifier,
int imageIndex)
std::string imageUrl)
{
using namespace openspace;
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
TargetBrowserPair* pair = module->pair(identifier);
if (pair) {
pair->browser()->removeSelectedImage(imageIndex);
pair->browser()->removeSelectedImage(imageUrl);
}
}
@@ -771,13 +775,13 @@ namespace {
LINFO("Image collection is loaded in Screen Space Sky Browser " + identifier);
pair->setImageCollectionIsLoaded(true);
// Add all selected images to WorldWide Telescope
const std::vector<int>& images = pair->selectedImages();
const std::vector<std::string>& images = pair->selectedImages();
std::for_each(
images.rbegin(), images.rend(),
[&](int index) {
const ImageData& image = module->wwtDataHandler().image(index);
[&](std::string imageUrl) {
const ImageData& image = module->wwtDataHandler().image(imageUrl).value();
// Index of image is used as layer ID as it's unique in the image data set
pair->browser()->addImageLayerToWwt(image.imageUrl, index);
pair->browser()->addImageLayerToWwt(image.imageUrl);
}
);
}

View File

@@ -69,8 +69,8 @@ TargetBrowserPair::TargetBrowserPair(SceneGraphNode* targetNode,
_targetRenderable = dynamic_cast<RenderableSkyTarget*>(_targetNode->renderable());
}
void TargetBrowserPair::setImageOrder(int i, int order) {
_browser->setImageOrder(i, order);
void TargetBrowserPair::setImageOrder(const std::string& imageUrl, int order) {
_browser->setImageOrder(imageUrl, order);
}
void TargetBrowserPair::startFinetuningTarget() {
@@ -151,13 +151,23 @@ double TargetBrowserPair::verticalFov() const {
return _browser->verticalFov();
}
std::vector<int> TargetBrowserPair::selectedImages() const {
std::vector<std::string> TargetBrowserPair::selectedImages() const {
return _browser->selectedImages();
}
ghoul::Dictionary TargetBrowserPair::dataAsDictionary() const {
glm::dvec2 spherical = targetDirectionEquatorial();
glm::dvec3 cartesian = skybrowser::sphericalToCartesian(spherical);
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
std::vector<std::string> selectedImagesIndices;
for (const std::string& imageUrl : selectedImages()) {
bool imageExists = module->wwtDataHandler().image(imageUrl).has_value();
ghoul_assert(imageExists, "Image doesn't exist in the wwt catalog!");
selectedImagesIndices.push_back(
module->wwtDataHandler().image(imageUrl)->identifier
);
}
ghoul::Dictionary res;
res.setValue("id", browserId());
@@ -172,7 +182,7 @@ ghoul::Dictionary TargetBrowserPair::dataAsDictionary() const {
res.setValue("ratio", static_cast<double>(_browser->browserRatio()));
res.setValue("isFacingCamera", isFacingCamera());
res.setValue("isUsingRae", isUsingRadiusAzimuthElevation());
res.setValue("selectedImages", selectedImages());
res.setValue("selectedImages", selectedImagesIndices);
res.setValue("scale", static_cast<double>(_browser->scale()));
res.setValue("opacities", _browser->opacities());
res.setValue("borderRadius", _browser->borderRadius());
@@ -193,9 +203,9 @@ ghoul::Dictionary TargetBrowserPair::dataAsDictionary() const {
return res;
}
void TargetBrowserPair::selectImage(const ImageData& image, int i) {
void TargetBrowserPair::selectImage(const ImageData& image) {
// Load image into browser
_browser->selectImage(image.imageUrl, i);
_browser->selectImage(image.imageUrl);
// If the image has coordinates, move the target
if (image.hasCelestialCoords) {
@@ -205,20 +215,20 @@ void TargetBrowserPair::selectImage(const ImageData& image, int i) {
}
}
void TargetBrowserPair::addImageLayerToWwt(const std::string& url, int i) {
_browser->addImageLayerToWwt(url, i);
void TargetBrowserPair::addImageLayerToWwt(const std::string& imageUrl) {
_browser->addImageLayerToWwt(imageUrl);
}
void TargetBrowserPair::removeSelectedImage(int i) {
_browser->removeSelectedImage(i);
void TargetBrowserPair::removeSelectedImage(const std::string& imageUrl) {
_browser->removeSelectedImage(imageUrl);
}
void TargetBrowserPair::loadImageCollection(const std::string& collection) {
_browser->loadImageCollection(collection);
}
void TargetBrowserPair::setImageOpacity(int i, float opacity) {
_browser->setImageOpacity(i, opacity);
void TargetBrowserPair::setImageOpacity(const std::string& imageUrl, float opacity) {
_browser->setImageOpacity(imageUrl, opacity);
}
void TargetBrowserPair::hideChromeInterface() {

View File

@@ -69,12 +69,12 @@ namespace {
return msg;
}
ghoul::Dictionary addImageMessage(const std::string& id, const std::string& url) {
ghoul::Dictionary addImageMessage(const std::string& url) {
using namespace std::string_literals;
ghoul::Dictionary msg;
msg.setValue("event", "image_layer_create"s);
msg.setValue("id", id);
msg.setValue("id", url);
msg.setValue("url", url);
msg.setValue("mode", "preloaded"s);
msg.setValue("goto", false);
@@ -101,7 +101,7 @@ namespace {
return msg;
}
ghoul::Dictionary setLayerOrderMessage(const std::string& id, int order) {
ghoul::Dictionary setLayerOrderMessage(const std::string& imageUrl, int order) {
static int MessageCounter = 0;
// The lower the layer order, the more towards the back the image is placed
@@ -110,7 +110,7 @@ namespace {
ghoul::Dictionary msg;
msg.setValue("event", "image_layer_order"s);
msg.setValue("id", id);
msg.setValue("id", imageUrl);
msg.setValue("order", order);
msg.setValue("version", MessageCounter);
@@ -165,33 +165,33 @@ void WwtCommunicator::update() {
Browser::update();
}
void WwtCommunicator::selectImage(const std::string& url, int i) {
void WwtCommunicator::selectImage(const std::string& url) {
// Ensure there are no duplicates
auto it = findSelectedImage(i);
auto it = findSelectedImage(url);
if (it == _selectedImages.end()) {
// Push newly selected image to front
_selectedImages.push_front(std::pair<int, double>(i, 1.0));
_selectedImages.push_front(std::pair<std::string, double>(url, 1.0));
// If wwt has not loaded the collection yet, wait with passing the message
if (_isImageCollectionLoaded) {
addImageLayerToWwt(url, i);
addImageLayerToWwt(url);
}
}
}
void WwtCommunicator::addImageLayerToWwt(const std::string& url, int i) {
void WwtCommunicator::addImageLayerToWwt(const std::string& imageUrl) {
// Index of image is used as layer ID as it is unique in the image data set
sendMessageToWwt(addImageMessage(std::to_string(i), url));
sendMessageToWwt(setImageOpacityMessage(std::to_string(i), 1.0));
sendMessageToWwt(addImageMessage(imageUrl));
sendMessageToWwt(setImageOpacityMessage(imageUrl, 1.0));
}
void WwtCommunicator::removeSelectedImage(int i) {
void WwtCommunicator::removeSelectedImage(const std::string& imageUrl) {
// Remove from selected list
auto it = findSelectedImage(i);
auto it = findSelectedImage(imageUrl);
if (it != _selectedImages.end()) {
_selectedImages.erase(it);
sendMessageToWwt(removeImageMessage(std::to_string(i)));
sendMessageToWwt(removeImageMessage(imageUrl));
}
}
@@ -200,14 +200,14 @@ void WwtCommunicator::sendMessageToWwt(const ghoul::Dictionary& msg) const {
executeJavascript(fmt::format("sendMessageToWWT({});", m));
}
std::vector<int> WwtCommunicator::selectedImages() const {
std::vector<int> selectedImagesVector;
std::vector<std::string> WwtCommunicator::selectedImages() const {
std::vector<std::string> selectedImagesVector;
selectedImagesVector.resize(_selectedImages.size());
std::transform(
_selectedImages.cbegin(),
_selectedImages.cend(),
selectedImagesVector.begin(),
[](const std::pair<int, double>& image) { return image.first; }
[](const std::pair<std::string, double>& image) { return image.first; }
);
return selectedImagesVector;
}
@@ -219,7 +219,7 @@ std::vector<double> WwtCommunicator::opacities() const {
_selectedImages.cbegin(),
_selectedImages.cend(),
opacities.begin(),
[](const std::pair<int, double>& image) { return image.second; }
[](const std::pair<std::string, double>& image) { return image.second; }
);
return opacities;
}
@@ -277,11 +277,15 @@ bool WwtCommunicator::isImageCollectionLoaded() const {
return _isImageCollectionLoaded;
}
std::deque<std::pair<int, double>>::iterator WwtCommunicator::findSelectedImage(int i) {
SelectedImageDeque::iterator WwtCommunicator::findSelectedImage(
const std::string& imageUrl)
{
auto it = std::find_if(
_selectedImages.begin(),
_selectedImages.end(),
[i](const std::pair<int, double>& pair) { return pair.first == i; }
[imageUrl](const std::pair<std::string, double>& pair) {
return pair.first == imageUrl;
}
);
return it;
}
@@ -290,12 +294,12 @@ glm::dvec2 WwtCommunicator::equatorialAim() const {
return _equatorialAim;
}
void WwtCommunicator::setImageOrder(int image, int order) {
void WwtCommunicator::setImageOrder(const std::string& imageUrl, int order) {
// Find in selected images list
auto current = findSelectedImage(image);
auto current = findSelectedImage(imageUrl);
int currentIndex = static_cast<int>(std::distance(_selectedImages.begin(), current));
std::deque<std::pair<int, double>> newDeque;
std::deque<std::pair<std::string, double>> newDeque;
for (int i = 0; i < static_cast<int>(_selectedImages.size()); i++) {
if (i == currentIndex) {
@@ -318,7 +322,7 @@ void WwtCommunicator::setImageOrder(int image, int order) {
_selectedImages = newDeque;
int reverseOrder = static_cast<int>(_selectedImages.size()) - order - 1;
ghoul::Dictionary message = setLayerOrderMessage(std::to_string(image), reverseOrder);
ghoul::Dictionary message = setLayerOrderMessage(imageUrl, reverseOrder);
sendMessageToWwt(message);
}
@@ -328,11 +332,11 @@ void WwtCommunicator::loadImageCollection(const std::string& collection) {
}
}
void WwtCommunicator::setImageOpacity(int i, float opacity) {
auto it = findSelectedImage(i);
void WwtCommunicator::setImageOpacity(const std::string& imageUrl, float opacity) {
auto it = findSelectedImage(imageUrl);
it->second = opacity;
ghoul::Dictionary msg = setImageOpacityMessage(std::to_string(i), opacity);
ghoul::Dictionary msg = setImageOpacityMessage(imageUrl, opacity);
sendMessageToWwt(msg);
}

View File

@@ -171,9 +171,8 @@ namespace {
return true;
}
std::optional<openspace::ImageData> loadImageFromNode(
const tinyxml2::XMLElement* node,
std::string collection)
std::optional<openspace::ImageData>
loadImageFromNode(const tinyxml2::XMLElement* node, const std::string& collection)
{
using namespace openspace;
@@ -243,7 +242,8 @@ namespace {
hasCelestialCoords,
fov,
equatorialSpherical,
equatorialCartesian
equatorialCartesian,
""
};
}
} //namespace
@@ -318,13 +318,21 @@ void WwtDataHandler::loadImages(const std::string& root,
saveImagesFromXml(rootNode, collectionName);
}
}
// Sort images in alphabetical order
std::sort(
_images.begin(),
_images.end(),
[](ImageData& a, ImageData& b) { return a.name < b.name; }
// Sort images. Copy images to vector
std::vector<ImageData> _imageVector;
for (const auto& [id, img] : _images) {
_imageVector.push_back(img);
}
// Sort
std::sort(_imageVector.begin(), _imageVector.end(),
[](const ImageData& lhs, const ImageData& rhs) {
return lhs.name < rhs.name;
}
);
// Set the identifiers to the correct order
for (int i = 0; i < _imageVector.size(); i++) {
_images[_imageVector[i].imageUrl].identifier = std::to_string(i);
}
LINFO(fmt::format("Loaded {} WorldWide Telescope images", _images.size()));
}
@@ -333,9 +341,16 @@ int WwtDataHandler::nLoadedImages() const {
return static_cast<int>(_images.size());
}
const ImageData& WwtDataHandler::image(int i) const {
ghoul_assert(i < static_cast<int>(_images.size()), "Index outside of vector size");
return _images[i];
std::optional<const ImageData> WwtDataHandler::image(const std::string& imageUrl) const {
auto it = _images.find(imageUrl);
if (it == _images.end()) {
return std::nullopt;
}
return it->second;
}
const std::map<std::string, ImageData>& WwtDataHandler::images() const {
return _images;
}
void WwtDataHandler::saveImagesFromXml(const tinyxml2::XMLElement* root,
@@ -350,9 +365,11 @@ void WwtDataHandler::saveImagesFromXml(const tinyxml2::XMLElement* root,
const std::string name = node->Name();
// If node is an image or place, load it
if (name == ImageSet || name == Place) {
std::optional<ImageData> image = loadImageFromNode(node, collection);
std::optional<ImageData> image = loadImageFromNode(
node, collection
);
if (image.has_value()) {
_images.push_back(std::move(*image));
_images.insert({ image.value().imageUrl, std::move(*image) });
}
}