Skip to content

Commit

Permalink
Add FrameDict class
Browse files Browse the repository at this point in the history
  • Loading branch information
r-owen committed Nov 9, 2017
1 parent e2d8aec commit d56ab0d
Show file tree
Hide file tree
Showing 12 changed files with 923 additions and 11 deletions.
1 change: 1 addition & 0 deletions doc/mainpage.dox
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ that support time, spectra and tables have yet been wrapped. The python wrapper
## Differences between this and Starlink AST

- Most functions in AST are classes or methods in this shim.
- New class `FrameDict` which is a `FrameSet` that can reference frames by domain name.
- `Mapping::applyForward` and `Mapping::applyInverse` methods transform single points or lists of points.
These replace AST's `astTran<X>` functions and no `invert` flag is supported.
There are three versions of each method:
Expand Down
2 changes: 1 addition & 1 deletion include/astshim/Frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ class Frame : public Mapping {
/**
Set @ref Frame_Domain "Domain": coordinate system domain
*/
void setDomain(std::string const &domain) { setC("Domain", domain); }
virtual void setDomain(std::string const &domain) { setC("Domain", domain); }

/**
Set @ref Frame_Dut1 "Dut1": difference between the UT1 and UTC timescale (sec)
Expand Down
299 changes: 299 additions & 0 deletions include/astshim/FrameDict.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
/*
* LSST Data Management System
* Copyright 2017 AURA/LSST.
*
* This product includes software developed by the
* LSST Project (http://www.lsst.org/).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the LSST License Statement and
* the GNU General Public License along with this program. If not,
* see <https://www.lsstcorp.org/LegalNotices/>.
*/
#ifndef ASTSHIM_FRAMEDICT_H
#define ASTSHIM_FRAMEDICT_H

#include <cassert>
#include <memory>
#include <set>
#include <sstream>
#include <stdexcept>
#include <unordered_map>

#include "astshim/base.h"
#include "astshim/detail/utils.h"
#include "astshim/Frame.h"
#include "astshim/FrameSet.h"

namespace ast {

/**
A FrameSet whose frames can be referenced by domain name.
For every FrameSet method that takes a frame index, the same method in FrameDict may take a (case blind)
domain name or a frame index.
This has several useful applications:
- Locate a frame without changing the FrameSet (findFrame is not a const method).
- Locate a frame or mapping in a way that is unaffected by deleting frames
(indices change, domain names do not).
All contained Frames with explicitly set, non-empty domains must have unique domains (where the comparison
ignores case). Use FrameSet if you want a collection of Frames that may have matching domains.
@warning FrameDict.getClassName returns "FrameSet". This is because FrameDict has no direct AST equivalent;
it is merely a convenience wrapper around FrameSet.
@note
- AST casts all Frame domains to uppercase. This is why domain comparison and domain lookup are case blind.
- Some AST frame classes have default domain names, e.g. SkyFrame defaults to "SKY". Such default
names are ignored in order to reduce the chance of accidental collisions.
### Attributes
All those of FrameSet.
*/
class FrameDict : public FrameSet {
friend class Object;

public:
/**
Construct a FrameDict from a single Frame
The frame is deep copied.
@param[in] frame the first @ref Frame to be inserted into the @ref FrameDict.
This initially becomes both the base and the current Frame.
Further Frames may be added using @ref addFrame
@param[in] options Comma-separated list of attribute assignments.
*/
explicit FrameDict(Frame const &frame, std::string const &options = "") : FrameSet(frame, options) {
_domainIndexDict = _makeNewDict(*this);
}

/**
Construct a FrameDict from two frames and a mapping that connects them
Both frames and the mapping are deep copied.
@param[in] baseFrame base @ref Frame.
@param[in] mapping mapping connecting baseFrame to currentFrame.
@param[in] currentFrame current @ref Frame.
@param[in] options Comma-separated list of attribute assignments.
@throws std::invalid_argument if both Frames have the same non-empty domain.
*/
explicit FrameDict(Frame const &baseFrame, Mapping const &mapping, Frame const &currentFrame,
std::string const &options = "")
: FrameSet(baseFrame, mapping, currentFrame) {
_domainIndexDict = _makeNewDict(*this);
}

/**
Construct a FrameDict from a FrameSet
The FrameSet is deep-copied
@throws std::invalid_argument if two Frames in the FrameSet have the same non-empty domain.
*/
explicit FrameDict(FrameSet const &frameSet)
: FrameDict(reinterpret_cast<AstFrameSet *>(frameSet.copy()->getRawPtr())) {}
virtual ~FrameDict() {}

FrameDict(FrameDict const &) = delete;
FrameDict(FrameDict &&) = default;
FrameDict &operator=(FrameDict const &) = delete;
FrameDict &operator=(FrameDict &&) = default;

/// Return a deep copy of this object.
std::shared_ptr<FrameDict> copy() const { return std::static_pointer_cast<FrameDict>(copyPolymorphic()); }

/**
@copydoc FrameSet::addFrame
@throws std::invalid_argument if `frame` has a non-empty domain and this FrameDict already
contains a Frame with that domain
*/
void addFrame(int iframe, Mapping const &map, Frame const &frame) override;

/**
Variant of @ref addFrame(int, Mapping const &, Frame const &) "addFrame(int, ...)"
where the initial frame is specified by domain.
*/
void addFrame(std::string const &domain, Mapping const &map, Frame const &frame);

/**
Get the domain names for all contained Frames (excluding frames with empty or defaulted domain names).
*/
std::set<std::string> getAllDomains() const;

using FrameSet::getFrame;

/**
Variant of @ref getFrame "getFrame(int, bool)" where the frame is specified by domain name.
@throw std::out_of_range if no frame found with the specified domain
*/
std::shared_ptr<Frame> getFrame(std::string const &domain, bool copy = true) const {
return getFrame(getIndex(domain), copy);
}

using FrameSet::getMapping;

/**
Variant of @ref getMapping(int, int) with the second frame specified by domain.
@throw std::out_of_range if no frame found with the specified from or to domain
*/
std::shared_ptr<Mapping> getMapping(int from, std::string const &to) const {
return getMapping(from, getIndex(to));
}

/**
Variant of @ref getMapping(int, int) with the first frame specified by domain.
@throw std::out_of_range if no frame found with the specified from or to domain
*/
std::shared_ptr<Mapping> getMapping(std::string const &from, int to) const {
return getMapping(getIndex(from), to);
}

/**
Variant of @ref getMapping(int, int) with the both frames specified by domain.
@throw std::out_of_range if no frame found with the specified from or to domain
*/
std::shared_ptr<Mapping> getMapping(std::string const &from, std::string const &to) const {
return getMapping(getIndex(from), getIndex(to));
}

/**
Get the index of a frame specified by domain
@throw std::out_of_range if no frame found with the specified domain
*/
int getIndex(std::string const &domain) const {
auto domainUpper = detail::stringToUpper(domain);
auto it = _domainIndexDict.find(domainUpper);
if (it == _domainIndexDict.end()) {
throw std::out_of_range("No frame found with domain " + domain);
}
return it->second;
}

/**
Return True if a frame in this FrameDict has the specified domain
*/
bool hasDomain(std::string const &domain) const { return _domainIndexDict.count(domain) > 0; }

using FrameSet::mirrorVariants;

/**
Variant of @ref mirrorVariants(int) with the frame specified by domain
@throw std::out_of_range if no frame found with the specified domain
*/
void mirrorVariants(std::string const &domain) { mirrorVariants(getIndex(domain)); }

using FrameSet::remapFrame;

/**
Variant of @ref remapFrame(int, Mapping&) with the frame specified by domain
@throw std::out_of_range if no frame found with the specified domain
*/
void remapFrame(std::string const &domain, Mapping &map) { remapFrame(getIndex(domain), map); }

/// @copydoc FrameSet::removeFrame
void removeFrame(int iframe) override;

/**
Variant of @ref removeFrame(int) with the frame specified by domain
@throw std::out_of_range if no frame found with the specified domain
*/
void removeFrame(std::string const &domain);

using FrameSet::setBase;

/**
Variant of @ref setBase(int) with the frame specified by domain
@throw std::out_of_range if no frame found with the specified domain
*/
void setBase(std::string const &domain) { setBase(getIndex(domain)); }

using FrameSet::setCurrent;

/**
Variant of @ref setCurrent(int) with the frame specified by domain
@throw std::out_of_range if no frame found with the specified domain
*/
void setCurrent(std::string const &domain) { setCurrent(getIndex(domain)); }

/**
Set the domain of the current frame (and update the internal dict).
@throws std::invalid_argument if another frame already has this domain
*/
void setDomain(std::string const &domain) override;

protected:
virtual std::shared_ptr<Object> copyPolymorphic() const override {
return copyImpl<FrameDict, AstFrameSet>();
}

/*
Return a copy as a FrameSet
This is used internally for operations that modify the contents:
- Retrieve a copy as a FrameSet
- Modify the copy
- Call _update to construct a new domain:index dict from the copy and swap in the new data
This could be a public member function, but wait until a need is identified.
Warning: this must return a FrameSet, not a FrameDict in order to avoid an infinite loop.
*/
std::shared_ptr<FrameSet> getFrameSet() const {
return std::static_pointer_cast<FrameSet>(copyImpl<FrameSet, AstFrameSet>());
}

/// Construct a FrameDict from a raw AST pointer
explicit FrameDict(AstFrameSet *rawptr);

private:
/*
Build a new domain:index dict from a FrameSet
*/
static std::unordered_map<std::string, int> _makeNewDict(FrameSet const &frameSet);

/*
Update the internal data using the supplied frameSet, which is swapped with the old data.
Build a new domain::index dict from frameSet. If successful, replace the current dict
and swap frameSet with the old frame set data.
If any exception is thrown this FrameDict (and the frameSet argument) is unchanged.
*/
void _update(FrameSet &frameSet) {
_domainIndexDict = _makeNewDict(frameSet);
this->swapRawPointers(frameSet);
}

std::unordered_map<std::string, int> _domainIndexDict; // Dict of frame domain:index
};

} // namespace ast

#endif
23 changes: 15 additions & 8 deletions include/astshim/FrameSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class FrameSet : public Frame {
explicit FrameSet(Frame const &baseFrame, Mapping const &mapping, Frame const &currentFrame,
std::string const &options = "")
: FrameSet(astFrameSet(baseFrame.copy()->getRawPtr(), "%s", options.c_str())) {
addFrame(1, mapping, currentFrame);
_basicAddFrame(1, mapping, currentFrame);
}

virtual ~FrameSet() {}
Expand Down Expand Up @@ -206,12 +206,7 @@ class FrameSet : public Frame {
the current @ref Frame in this FrameSet.
*/
virtual void addFrame(int iframe, Mapping const &map, Frame const &frame) {
if (iframe == AST__ALLFRAMES) {
throw std::runtime_error("iframe = AST__ALLFRAMES; call addAxes instead");
}
// astAddFrame makes deep copies of the map and frame, so no need to do anything extra
astAddFrame(getRawPtr(), iframe, map.getRawPtr(), frame.getRawPtr());
assertOK();
_basicAddFrame(iframe, map, frame);
}

/**
Expand Down Expand Up @@ -442,7 +437,7 @@ class FrameSet : public Frame {
@throws std::runtime_error if you attempt to remove the last frame
*/
void removeFrame(int iframe) {
virtual void removeFrame(int iframe) {
astRemoveFrame(getRawPtr(), iframe);
assertOK();
}
Expand Down Expand Up @@ -497,6 +492,18 @@ class FrameSet : public Frame {
throw std::invalid_argument(os.str());
}
}

private:
// non-virtual version of addFrame for use by constructors
void _basicAddFrame(int iframe, Mapping const &map, Frame const &frame) {
if (iframe == AST__ALLFRAMES) {
throw std::runtime_error("iframe = AST__ALLFRAMES; call addAxes instead");
}
// astAddFrame makes deep copies of the map and frame, so no need to do anything extra
astAddFrame(getRawPtr(), iframe, map.getRawPtr(), frame.getRawPtr());
assertOK();
}

};

} // namespace ast
Expand Down

0 comments on commit d56ab0d

Please sign in to comment.