commit 689dbf365c8af58c2483a11174bff457cdfe4ccf Author: Martchus Date: Wed Jun 24 23:20:44 2015 +0200 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8cdb845 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + 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 2 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 GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..53ff795 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# geocoordinatecalculator +Command line tool for basic calculations with geo coordinates such as format conversions +and calculation of distance, bearing, mid point, destination and more. + +## Build instructions +The application depends on c++utilities and is built in the same way. diff --git a/angle.cpp b/angle.cpp new file mode 100644 index 0000000..f0970e0 --- /dev/null +++ b/angle.cpp @@ -0,0 +1,163 @@ +#include "utils.h" +#include "angle.h" + +#include + +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265359 +#endif + +using namespace std; +using namespace ApplicationUtilities; + +Angle::Angle() : + m_val(0) +{ +} + +Angle::Angle(double value, AngularMeasure measure) : + m_val(0) +{ + switch(measure) + { + case AngularMeasure::Radian: + m_val = value; + break; + case AngularMeasure::Degree: + m_val = value * M_PI / 180.0; + } +} + +Angle::Angle(const string &value, AngularMeasure measure) : + m_val(0) +{ + switch(measure) { + case AngularMeasure::Radian: + m_val += ConversionUtilities::numberFromString(value); + break; + case AngularMeasure::Degree: + { + string::size_type mpos, spos = string::npos; + mpos = value.find(':'); + if(mpos == string::npos) + m_val += ConversionUtilities::numberFromString(value); + else if(mpos >= (value.length() - 1)) + throw Failure("excepted minutes after ':' in " + value); + else { + m_val += ConversionUtilities::numberFromString(value.substr(0, mpos)); + spos = value.find(':', mpos + 1); + if(spos == string::npos) + m_val += ConversionUtilities::numberFromString(value.substr(mpos + 1)) / 60.0; + else if(spos >= (value.length() - 1)) + throw Failure("excepted seconds after second ':'' in " + value); + else + m_val += (ConversionUtilities::numberFromString(value.substr(mpos + 1, spos - mpos - 1)) / 60.0) + + (ConversionUtilities::numberFromString(value.substr(spos + 1)) / 3600.0); + } + m_val = m_val * M_PI / 180.0; + break; + } + } +} + +double Angle::degreeValue() const +{ + return m_val * 180.0 / M_PI; +} + +double Angle::radianValue() const +{ + return m_val; +} + +bool Angle::isNull() const +{ + return m_val == 0.0; +} + +void Angle::adjust0To360() +{ + while(m_val < 0) m_val += 2.0 * M_PI; + while(m_val > 2.0 * M_PI) m_val -= 2.0 * M_PI; +} + +void Angle::adjust180To180() +{ + while(m_val > M_PI) m_val -= 2.0 * M_PI; + while(m_val < -M_PI) m_val += 2.0 * M_PI; +} + +void Angle::reverse() +{ + m_val += M_PI; + adjust0To360(); +} + +string Angle::toString(OutputForm format) const +{ + stringstream sstream(stringstream::in | stringstream::out); + sstream << setprecision(9); + double intpart, fractpart; + switch(format) { + case OutputForm::Degrees: + sstream << degreeValue(); + break; + case OutputForm::Minutes: + if(degreeValue() < 0) + sstream << "-"; + fractpart = modf(fabs(degreeValue()), &intpart); + fractpart *= 60; + sstream << intpart << ":" << fractpart; + break; + case OutputForm::Seconds: + if(degreeValue() < 0) + sstream << "-"; + fractpart = modf(fabs(degreeValue()), &intpart); + sstream << intpart << ":"; + fractpart *= 60; + fractpart = modf(fractpart, &intpart); + fractpart *= 60; + sstream << intpart << ":" << fractpart; + break; + case OutputForm::Radians: + sstream << radianValue(); + } + return sstream.str(); +} + +bool Angle::operator ==(const Angle &other) const +{ + return m_val == other.m_val; +} + +bool Angle::operator !=(const Angle &other) const +{ + return m_val != other.m_val; +} + +Angle Angle::operator +(const Angle &other) const +{ + return Angle(m_val + other.m_val); +} + +Angle Angle::operator -(const Angle &other) const +{ + return Angle(m_val - other.m_val); +} + +Angle &Angle::operator +=(const Angle &other) +{ + m_val += other.m_val; + return *this; +} + +Angle &Angle::operator -=(const Angle &other) +{ + m_val -= other.m_val; + return *this; +} diff --git a/angle.h b/angle.h new file mode 100644 index 0000000..f9e4ec8 --- /dev/null +++ b/angle.h @@ -0,0 +1,45 @@ +#ifndef COORDINATE_H +#define COORDINATE_H + +#include + +class Angle +{ +public: + enum class AngularMeasure + { + Radian, + Degree + }; + + enum class OutputForm + { + Degrees, + Minutes, + Seconds, + Radians + }; + + Angle(); + Angle(double value, AngularMeasure measure = AngularMeasure::Radian); + explicit Angle(const std::string &value, AngularMeasure measure = AngularMeasure::Radian); + double degreeValue() const; + double radianValue() const; + bool isNull() const; + void adjust0To360(); + void adjust180To180(); + void reverse(); + std::string toString() const; + std::string toString(OutputForm format) const; + + bool operator ==(const Angle &other) const; + bool operator !=(const Angle &other) const; + Angle operator +(const Angle &other) const; + Angle operator -(const Angle &other) const; + Angle &operator +=(const Angle &other); + Angle &operator -=(const Angle &other); +private: + double m_val; +}; + +#endif // COORDINATE_H diff --git a/general.pri b/general.pri new file mode 100644 index 0000000..9f790b4 --- /dev/null +++ b/general.pri @@ -0,0 +1,81 @@ +# template +TEMPLATE = lib +#dirs +UI_DIR = ./gui +MOC_DIR = ./moc +OBJECTS_DIR = ./obj +RCC_DIR = ./res +# compiler flags +QMAKE_CXXFLAGS += -std=c++11 +QMAKE_LFLAGS += -std=c++11 +unix { + QMAKE_LFLAGS += "-Wl,--rpath=./" +} +# prefix +targetprefix = $$(TARGET_PREFIX) +equals(targetprefix, "") { + win32 { + targetprefix = ../../.. + } else { + targetprefix = ../.. + } +} +message("Using target prefix \"$${targetprefix}\".") +# print install root +message("Using install root \"$$(INSTALL_ROOT)\".") +# target +CONFIG(debug, debug|release) { + TARGET = $$targetprefix/$${projectname}d +} else { + TARGET = $$targetprefix/$$projectname +} +# variables to check target architecture +win32-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch +win32-g++-32:QMAKE_TARGET.arch = x86 +win32-g++-64:QMAKE_TARGET.arch = x86_64 +linux-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch +linux-g++-32:QMAKE_TARGET.arch = x86 +linux-g++-64:QMAKE_TARGET.arch = x86_64 +# configuration +mobile { + DEFINES += CONFIG_MOBILE +} else:desktop { + DEFINES += CONFIG_DESKTOP +} else:android { + CONFIG += mobile + DEFINES += CONFIG_MOBILE +} else { + CONFIG += desktop + DEFINES += CONFIG_DESKTOP +} +no-gui { + QT -= gui + DEFINES += GUI_NONE + guiqtquick || guiqtwidgets { + error("Can not use no-gui with guiqtquick or guiqtwidgets.") + } else { + message("Configured for no GUI support.") + } +} else { + QT += gui + mobile { + CONFIG += guiqtquick + } + desktop { + CONFIG += guiqtwidgets + } +} +guiqtquick { + message("Configured for Qt Quick GUI support.") + greaterThan(QT_MAJOR_VERSION, 4): QT += quick + CONFIG(debug, debug|release) { + CONFIG += qml_debug + } + DEFINES += GUI_QTQUICK +} +guiqtwidgets { + message("Configured for Qt widgets GUI support.") + greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + DEFINES += GUI_QTWIDGETS + DEFINES += MODEL_UNDO_SUPPORT +} diff --git a/geocoordinatecalculator.pro b/geocoordinatecalculator.pro new file mode 100644 index 0000000..5fb30e1 --- /dev/null +++ b/geocoordinatecalculator.pro @@ -0,0 +1,44 @@ +projectname = geocoordinatecalculator +VERSION = 1.0.0 + +CONFIG += no-gui + +# include ../../common.pri when building as part of a subdirs project; otherwise include general.pri +!include(../../common.pri) { + !include(./general.pri) { + error("Couldn't find the common.pri or the general.pri file!") + } +} + +TEMPLATE = app + +CONFIG -= qt + +SOURCES += main.cpp \ + location.cpp \ + angle.cpp \ + utils.cpp + +HEADERS += \ + main.h \ + location.h \ + angle.h \ + utils.h + +RESOURCES += images.qrc + +OTHER_FILES += \ + README.md \ + LICENSE + +CONFIG(debug, debug|release) { + LIBS += -L../../ -lc++utilitiesd +} else { + LIBS += -L../../ -lc++utilities +} + +INCLUDEPATH += ../ + +# installs +target.path = $$(INSTALL_ROOT)/bin +INSTALLS += target diff --git a/location.cpp b/location.cpp new file mode 100644 index 0000000..0a06b38 --- /dev/null +++ b/location.cpp @@ -0,0 +1,318 @@ +#include "location.h" +#include "utils.h" + +#include + +#include +#include +#include + +using namespace std; +using namespace ApplicationUtilities; + +// WGS84 Parameters +#define WGS84_A 6378137.0 // major axis +#define WGS84_B 6356752.31424518 // minor axis +#define WGS84_F 0.0033528107 // ellipsoid flattening +#define WGS84_E 0.0818191908 // first eccentricity +#define WGS84_EP 0.0820944379 // second eccentricity + +// UTM Parameters +#define UTM_K0 0.9996 // scale factor +#define UTM_FE 500000.0 // false easting +#define UTM_FN_N 0.0 // false northing, northern hemisphere +#define UTM_FN_S 10000000.0 // false northing, southern hemisphere +#define UTM_E2 (WGS84_E * WGS84_E) // e^2 +#define UTM_E4 (UTM_E2 * UTM_E2) // e^4 +#define UTM_E6 (UTM_E4 * UTM_E2) // e^6 +#define UTM_EP2 (UTM_E2 / (1 - UTM_E2)) // e'^2 + +Location::Location() : + m_lat(0.0), + m_lon(0.0), + m_ele(0.0) +{} + +Location::Location(const Angle &latitude, const Angle &lon) : + m_lat(latitude), + m_lon(lon), + m_ele(0.0) +{} + +Location::Location(const string &lat, const string &lon, Angle::AngularMeasure measure) : + m_lat(Angle(lat, measure)), + m_lon(Angle(lon, measure)), + m_ele(0.0) +{} + +Location::Location(const string &latitudeAndLongitude, Angle::AngularMeasure measure) : + m_ele(0.0) +{ + string::size_type dpos = latitudeAndLongitude.find(','); + if(dpos == string::npos) + throw Failure("Pair of coordinates (latitude and longitude) required."); + else if(dpos >= (latitudeAndLongitude.length() - 1)) + throw Failure("No second longitude following after comma."); + else if(latitudeAndLongitude.find(',', dpos + 1) != string::npos) + throw Failure("More then 2 coordinates given."); + m_lat = Angle(latitudeAndLongitude.substr(0, dpos), measure); + m_lon = Angle(latitudeAndLongitude.substr(dpos + 1), measure); +} + +Location::~Location() +{} + +string Location::toString(Angle::OutputForm form) const +{ + return m_lat.toString(form) + "," + m_lon.toString(form); +} + +string Location::toUtmWgs4String() const +{ + int zone; + char zoneDesignator; + double east, north; + computeUtmWgs4Coordinates(zone, zoneDesignator, east, north); + stringstream ss(stringstream::in | stringstream::out); + ss << setprecision(0) << fixed; + ss << zone << zoneDesignator << "E" << east << "N" << north; + return ss.str(); +} + +double Location::distanceTo(const Location &location) const +{ + double lat1 = m_lat.radianValue(); + double lon1 = m_lon.radianValue(); + double lat2 = location.m_lat.radianValue(); + double lon2 = location.m_lon.radianValue(); + double latd = lat1 - lat2; + double lond = lon1 - lon2; + latd = sin(latd / 2.0); + lond = sin(lond / 2.0); + double a = latd * latd + lond * lond * cos(lat1) * cos(lat2); + return m_er * 2.0 * atan2(sqrt(a), sqrt(1.0 - a)); +} + +Angle Location::initialBearingTo(const Location &location) const +{ + double lat1 = m_lat.radianValue(); + double lon1 = m_lon.radianValue(); + double lat2 = location.m_lat.radianValue(); + double lon2 = location.m_lon.radianValue(); + double lond = lon2 - lon1; + double b = atan2(sin(lond) * cos(lat2), cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lond)); + Angle angle(b); + angle.adjust0To360(); + return angle; +} + +Angle Location::finalBearingTo(const Location &location) const +{ + Angle angle(location.initialBearingTo(*this)); + angle.reverse(); + return angle; +} + +Location Location::destination(double distance, const Angle &bearing) +{ + double lat1 = m_lat.radianValue(); + double lon1 = m_lon.radianValue(); + double brng = bearing.radianValue(); + double ad = angularDistance(distance).radianValue(); + double lat2 = asin(sin(lat1) * cos(ad) + cos(lat1) * sin(ad) * cos(brng)); + double lon2 = lon1 + atan2(sin(brng) * sin(ad) * cos(lat1), cos(ad) - sin(lat1) * sin(lat2)); + return Location(Angle(lat2), Angle(lon2)); +} + +void Location::computeUtmWgs4Coordinates(int &zone, char &zoneDesignator, double &east, double &north) const +{ + double a = WGS84_A; + double eccSquared = UTM_E2; + double k0 = UTM_K0; + + double latd = m_lat.degreeValue(); + double lond = m_lon.degreeValue(); + double latr = m_lat.radianValue(); + double lonr = m_lon.radianValue(); + + zone = int((lond + 180) / 6) + 1; + + if(latd >= 56.0 && latd < 64.0 && lond >= 3.0 && lond < 12.0) + zone = 32; + + // Special zones for Svalbard + if(latd >= 72.0 && latd < 84.0) + { + if(lond >= 0.0 && lond < 9.0) zone = 31; + else if(lond >= 9.0 && lond < 21.0) zone = 33; + else if(lond >= 21.0 && lond < 33.0) zone = 35; + else if(lond >= 33.0 && lond < 42.0) zone = 37; + } + + zoneDesignator = computeUtmZoneDesignator(); + + // +3 puts origin in middle of zone + double lonOriginr = Angle((zone - 1) * 6 - 180 + 3, Angle::AngularMeasure::Degree).radianValue(); + + double eccPrimeSquared = (eccSquared) / (1 - eccSquared); + double N = a / sqrt(1 - eccSquared * sin(latr) * sin(latr)); + double T = tan(latr) * tan(latr); + double C = eccPrimeSquared * cos(latr) * cos(latr); + double A = cos(latr) * (lonr-lonOriginr); + double M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 + - 5 * eccSquared * eccSquared * eccSquared / 256) * latr + - (3 * eccSquared / 8 + 3 * eccSquared*eccSquared / 32 + + 45 * eccSquared * eccSquared * eccSquared / 1024)*sin(2 * latr) + + (15 * eccSquared * eccSquared / 256 + + 45 * eccSquared * eccSquared*eccSquared / 1024) * sin(4 * latr) + - (35 * eccSquared * eccSquared * eccSquared/3072) * sin(6 * latr)); + + east = static_cast + (k0 * N * (A + (1 - T + C) * A * A * A / 6 + + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) + + 500000.0); + + north = static_cast + (k0 * (M + N * tan(latr) + * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A *A / 24 + + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720))); + + if(latd < 0) + north += 10000000.0; +} + +Location Location::midpoint(const Location &location1, const Location &location2) +{ + double lat1 = location1.m_lat.radianValue(); + double lon1 = location1.m_lon.radianValue(); + double lat2 = location2.m_lat.radianValue(); + double lon2 = location2.m_lon.radianValue(); + double lond = lon2 - lon1; + double x = cos(lat2) * cos(lond); + double y = cos(lat2) * sin(lond); + return Location( + Angle(atan2(sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y))), + Angle(lon1 + atan2(y, cos(lat1) + x))); +} + +double Location::trackLength(const std::vector &track, bool circle) +{ + if(track.size() < 2) + throw Failure("At least two locations are required to calculate a distance."); + + const Location *location1 = &track.at(0); + const Location *location2 = &track.at(1); + double distance = location1->distanceTo(*location2); + + for(std::vector::const_iterator i = track.cbegin() + 2, end = track.cend(); i != end; ++i) { + location1 = location2; + location2 = &(*i); + distance += location1->distanceTo(*location2); + } + + if(circle) + distance += track.front().distanceTo(track.back()); + + return distance; +} + +double Location::earthRadius() +{ + return m_er; +} + +Angle Location::angularDistance(double distance) +{ + return Angle(distance / m_er); +} + +char Location::computeUtmZoneDesignator() const +{ + double l = m_lat.degreeValue(); + if ((84 >= l) && (l >= 72)) return 'X'; + else if ((72 > l) && (l >= 64)) return 'W'; + else if ((64 > l) && (l >= 56)) return 'V'; + else if ((56 > l) && (l >= 48)) return 'U'; + else if ((48 > l) && (l >= 40)) return 'T'; + else if ((40 > l) && (l >= 32)) return 'S'; + else if ((32 > l) && (l >= 24)) return 'R'; + else if ((24 > l) && (l >= 16)) return 'Q'; + else if ((16 > l) && (l >= 8)) return 'P'; + else if (( 8 > l) && (l >= 0)) return 'N'; + else if (( 0 > l) && (l >= -8)) return 'M'; + else if ((-8 > l) && (l >= -16)) return 'L'; + else if((-16 > l) && (l >= -24)) return 'K'; + else if((-24 > l) && (l >= -32)) return 'J'; + else if((-32 > l) && (l >= -40)) return 'H'; + else if((-40 > l) && (l >= -48)) return 'G'; + else if((-48 > l) && (l >= -56)) return 'F'; + else if((-56 > l) && (l >= -64)) return 'E'; + else if((-64 > l) && (l >= -72)) return 'D'; + else if((-72 > l) && (l >= -80)) return 'C'; + else return '\0'; +} + +void Location::setValueByProvidedUtmWgs4Coordinates(const string &utmWgs4Coordinates) +{ + string::size_type epos = utmWgs4Coordinates.find('E'); + if(epos != 0 && epos != string::npos) { + string::size_type npos = utmWgs4Coordinates.find('N', epos); + if(npos < (utmWgs4Coordinates.length() - 1) && npos != string::npos) { + int zone = ConversionUtilities::numberFromString(utmWgs4Coordinates.substr(0, epos - 1)); + char zoneDesignator = utmWgs4Coordinates.at(epos - 1); + double east = ConversionUtilities::numberFromString(utmWgs4Coordinates.substr(epos + 1, npos - epos - 1)); + double north = ConversionUtilities::numberFromString(utmWgs4Coordinates.substr(npos + 1)); + setValueByProvidedUtmWgs4Coordinates(zone, zoneDesignator, east, north); + return; + } + } + throw Failure("UTM coordinates incomplete."); +} + +void Location::setValueByProvidedUtmWgs4Coordinates(int zone, char zoneDesignator, double easting, double northing) +{ + double k0 = UTM_K0; + double a = WGS84_A; + double eccSquared = UTM_E2; + double eccPrimeSquared; + double e1 = (1 - sqrt(1 - eccSquared)) / (1 + sqrt(1 - eccSquared)); + + double x = easting - 500000.0; //remove 500,000 meter offset for longitude + double y = northing; + + if((zoneDesignator - 'N') < 0) + //remove 10,000,000 meter offset used for southern hemisphere + y -= 10000000.0; + + //+3 puts origin in middle of zone + double longOriginr = Angle((zone - 1) * 6 - 180 + 3, Angle::AngularMeasure::Degree).radianValue(); + eccPrimeSquared = (eccSquared)/(1-eccSquared); + + double M = y / k0; + double mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 + -5 * eccSquared * eccSquared * eccSquared / 256)); + + double phi1Rad = mu + ((3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu) + + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu) + + (151 * e1 * e1 * e1 / 96) * sin(6 * mu)); + + double N1 = a / sqrt(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad)); + double T1 = tan(phi1Rad) * tan(phi1Rad); + double C1 = eccPrimeSquared * cos(phi1Rad) * cos(phi1Rad); + double R1 = a * (1 - eccSquared) / pow(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad), 1.5); + double D = x / (N1 * k0); + + m_lat = Angle(phi1Rad - ((N1 * tan(phi1Rad) / R1) + * (D * D / 2 + -(5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 + +(61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared + -3 * C1 * C1) * D * D * D * D * D * D / 720))); + m_lat.adjust180To180(); + m_lon = Angle(((D - (1 + 2 * T1 + C1) * D * D * D / 6 + +(5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) + * D * D * D * D * D / 120) + / cos(phi1Rad)) + longOriginr); + m_lon.adjust180To180(); +} + +const double Location::m_er = 6371000.0; diff --git a/location.h b/location.h new file mode 100644 index 0000000..10b5f29 --- /dev/null +++ b/location.h @@ -0,0 +1,88 @@ +#ifndef LOCATION_H +#define LOCATION_H + +#include "angle.h" + +#include +#include + +class Location +{ +public: + enum class GeographicCoordinateSystem + { + LatitudeAndLongitude, + UTMWGS84 + }; + + Location(); + Location(const Angle &lat, const Angle &lon); + explicit Location(const std::string &lat, const std::string &lon, Angle::AngularMeasure measure = Angle::AngularMeasure::Radian); + explicit Location(const std::string &latitudeAndLongitude, Angle::AngularMeasure measure = Angle::AngularMeasure::Radian); + ~Location(); + + const Angle &latitude() const; + void setLatitude(const Angle &value); + const Angle &longitude() const; + void setLongitude(const Angle &value); + double elevation() const; + void setElevation(double value); + std::string toString(Angle::OutputForm form = Angle::OutputForm::Degrees) const; + std::string toUtmWgs4String() const; + bool isEmpty() const; + double distanceTo(const Location &location) const; + Angle initialBearingTo(const Location &location) const; + Angle finalBearingTo(const Location &location) const; + Location destination(double distance, const Angle &bearing); + void computeUtmWgs4Coordinates(int &zone, char &zoneDesignator, double &east, double &north) const; + char computeUtmZoneDesignator() const; + void setValueByProvidedUtmWgs4Coordinates(const std::string &utmWgs4Coordinates); + void setValueByProvidedUtmWgs4Coordinates(int zone, char zoneDesignator, double east, double north); + static Location midpoint(const Location &location1, const Location &location2); + static double trackLength(const std::vector &track, bool circle = false); + static double earthRadius(); + static Angle angularDistance(double distance); +protected: +private: + Angle m_lat; + Angle m_lon; + double m_ele; + static const double m_er; +}; + +inline const Angle &Location::latitude() const +{ + return m_lat; +} + +inline const Angle &Location::longitude() const +{ + return m_lon; +} + +inline double Location::elevation() const +{ + return m_ele; +} + +inline void Location::setLatitude(const Angle &value) +{ + m_lat = value; +} + +inline void Location::setLongitude(const Angle &value) +{ + m_lon = value; +} + +inline void Location::setElevation(double value) +{ + m_ele = value; +} + +inline bool Location::isEmpty() const +{ + return m_lat.isNull() && m_lon.isNull() && m_ele == 0.0; +} + +#endif // LOCATION_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f2d8b58 --- /dev/null +++ b/main.cpp @@ -0,0 +1,326 @@ +#include "main.h" +#include "location.h" +#include "utils.h" + +#include +#include + +#include +#include +#include + +using namespace std; +using namespace ConversionUtilities; +using namespace ApplicationUtilities; + +Angle::AngularMeasure inputAngularMeasure = Angle::AngularMeasure::Degree; +Angle::OutputForm outputFormForAngles = Angle::OutputForm::Degrees; +SystemForLocations inputSystemForLocations = SystemForLocations::LatitudeLongitude; +SystemForLocations outputSystemForLocations = SystemForLocations::LatitudeLongitude; + +int main(int argc, char *argv[]) +{ + try { + ArgumentParser argparser; + + Argument convert("convert", "c", "Converts the given coordinate or location to the specified output form."); + convert.setRequiredValueCount(1); + convert.appendValueName("coordinate/location"); + + Argument distance("distance", "d", "Computes the approximate distance in meters between two locations."); + distance.setRequiredValueCount(2); + distance.appendValueName("location 1"); + distance.appendValueName("location 2"); + + Argument trackLength("track-length", "t", "Computes the approximate length in meters of a track given by a file containing trackpoints separated by new lines."); + trackLength.setRequiredValueCount(1); + trackLength.appendValueName("path"); + Argument circle("circle", "", "If present the distance between the first and the last trackpoints will be added to the total track length."); + trackLength.setSecondaryArguments({&circle}); + + Argument bearing("bearing", "b", "Computes the approximate initial bearing East of true North when traveling along the shortest path between the given locations."); + bearing.setRequiredValueCount(2); + bearing.appendValueName("location 1"); + bearing.appendValueName("location 2"); + + Argument fbearing("final-bearing", string(), "Computes the approximate final bearing East of true North when traveling along the shortest path between the given locations."); + fbearing.setRequiredValueCount(2); + fbearing.appendValueName("location 1"); + fbearing.appendValueName("location 2"); + + Argument midpoint("midpoint", "m", "Computes the approximate midpoint between the given locations."); + midpoint.setRequiredValueCount(2); + midpoint.appendValueName("location 1"); + midpoint.appendValueName("location 2"); + + Argument destination("destination", string(), "Calculates destination point given distance and bearing from start point."); + destination.setRequiredValueCount(3); + destination.appendValueName("start"); + destination.appendValueName("distance"); + destination.appendValueName("bearing"); + + Argument gmapsLink("gmaps-link", "", "Generates a Google Maps link for all locations given by a file containing locations separated by new lines."); + gmapsLink.setRequiredValueCount(1); + gmapsLink.appendValueName("path"); + + Argument inputAngularMeasureArg("input-angular-measure", "i", "Use this option to specify the angular measure you use to provide angles (degree or radian; default is degree)."); + inputAngularMeasureArg.setRequiredValueCount(1); + inputAngularMeasureArg.appendValueName("angular measure"); + inputAngularMeasureArg.setCombinable(true); + + Argument outputFormForAnglesArg("output-angle-form", "o", "Use this option to specify the output form for angles (degrees, minutes, seconds or radians; default is degrees)."); + outputFormForAnglesArg.setRequiredValueCount(1); + outputFormForAnglesArg.appendValueName("form"); + outputFormForAnglesArg.setCombinable(true); + + Argument inputSystemForLocationsArg("input-location-system", string(), "Use this option to specify the geographic system you use to provide locations (latitude&longitue or UTM-WGS84)."); + inputSystemForLocationsArg.setRequiredValueCount(1); + inputSystemForLocationsArg.appendValueName("system"); + inputSystemForLocationsArg.setCombinable(true); + + Argument outputSystemForLocationsArg("output-location-system", string(), "Use this option to specify which geographic system is used to display locations (latitude&longitue or UTM-WGS84)."); + outputSystemForLocationsArg.setRequiredValueCount(1); + outputSystemForLocationsArg.appendValueName("system"); + outputSystemForLocationsArg.setCombinable(true); + + HelpArgument help(argparser); + + Argument version("version", "v", "Shows the version of this application."); + + argparser.setMainArguments({&convert, &distance, &trackLength, &bearing, &fbearing, &midpoint, &destination, &gmapsLink, &help, &version, &inputAngularMeasureArg, &outputFormForAnglesArg, &inputSystemForLocationsArg, &outputSystemForLocationsArg}); + + argparser.parseArgs(argc, argv); + + if(inputAngularMeasureArg.isPresent()) { + if(inputAngularMeasureArg.value(0) == "radian") { + inputAngularMeasure = Angle::AngularMeasure::Radian; + } else if(inputAngularMeasureArg.value(0) == "degree") { + inputAngularMeasure = Angle::AngularMeasure::Degree; + } else { + cout << "Invalid angular measure given, see --help." << endl; + return 0; + } + } + + if(outputFormForAnglesArg.isPresent()) { + if(outputFormForAnglesArg.value(0) == "degrees") { + outputFormForAngles = Angle::OutputForm::Degrees; + } else if(outputFormForAnglesArg.value(0) == "minutes") { + outputFormForAngles = Angle::OutputForm::Minutes; + } else if(outputFormForAnglesArg.value(0) == "seconds") { + outputFormForAngles = Angle::OutputForm::Seconds; + } else if(outputFormForAnglesArg.value(0) == "radians") { + outputFormForAngles = Angle::OutputForm::Radians; + } else { + cout << "Invalid output form for angles given, see --help." << endl; + return 0; + } + } + + if(inputSystemForLocationsArg.isPresent()) { + if(inputSystemForLocationsArg.value(0) == "latitude&longitue") { + inputSystemForLocations = SystemForLocations::LatitudeLongitude; + } else if(inputSystemForLocationsArg.value(0) == "UTM-WGS84") { + inputSystemForLocations = SystemForLocations::UTMWGS84; + } else { + cout << "Invalid geographic coordinate system given, see --help." << endl; + return 0; + } + } + + if(outputSystemForLocationsArg.isPresent()) { + if(outputSystemForLocationsArg.value(0) == "latitude&longitue") { + outputSystemForLocations = SystemForLocations::LatitudeLongitude; + } else if(outputSystemForLocationsArg.value(0) == "UTM-WGS84") { + outputSystemForLocations = SystemForLocations::UTMWGS84; + } else { + cout << "Invalid geographic coordinate system given, see --help." << endl; + return 0; + } + } + + try { + if(help.isPresent()) { + cout << endl; + printAngleFormatInfo(cout); + } else if(version.isPresent()) { + cout << "1.0.0"; + } else if(convert.isPresent()) { + printConversion(convert.value(0)); + } else if(distance.isPresent()) { + printDistance(distance.value(0), distance.value(1)); + } else if(trackLength.isPresent()) { + printTrackLength(trackLength.value(0), circle.isPresent()); + } else if(bearing.isPresent()) { + printBearing(bearing.value(0), bearing.value(1)); + } else if(fbearing.isPresent()) { + printFinalBearing(fbearing.value(0), fbearing.value(1)); + } else if(midpoint.isPresent()) { + printMidpoint(midpoint.value(0), midpoint.value(1)); + } else if(destination.isPresent()) { + printDestination(destination.value(0), destination.value(1), destination.value(2)); + } else if(gmapsLink.isPresent()) { + printMapsLink(gmapsLink.value(0)); + } else { + cout << "No arguments given. See --help for available commands."; + } + } catch(Failure &ex) { + cout << "The provided locations/coordinates couldn't be parsed correctly. " << ex.what() << endl; + cout << endl; + printAngleFormatInfo(cout); + } + } catch(Failure &ex) { + cout << "Unable to parse arguments. " << ex.what() << endl << "See --help for available commands."; + } + + cout << endl; + return 0; +} + + +Location locationFromString(const string &userInput) +{ + switch(inputSystemForLocations) { + case SystemForLocations::UTMWGS84: { + Location l; + l.setValueByProvidedUtmWgs4Coordinates(userInput); + return l; + } default: + return Location(userInput, inputAngularMeasure); + } +} + +vector locationsFromFile(const string &path) +{ + // prepare reading + fstream file; + file.open(path, ios_base::in); + if(!file) + throw ios_base::failure("Unable to open the file \"" + path + "\"."); + file.exceptions(ios_base::badbit); + string line; + vector locations; + while(getline(file, line)) { + if(line.empty() || line.at(0) == '#') + continue; // skip empty lines and comments + locations.push_back(locationFromString(line)); + } + return locations; +} + +void printAngleFormatInfo(ostream &os) +{ + os << "To provide a location/trackpoint, use the following form:\n"; + os << "latitude,longitude\n"; + + os << "\nUse one of the following forms to specify angles, if you use --input-angle-measure degree:\n"; + os << "[+-]DDD.DDDDD\n"; + os << "[+-]DDD:MM.MMMMM\n"; + os << "[+-]DDD:MM:SS.SSSSS\n"; + os << "D indicates degrees, M indicates minutes of arc, and S indicates seconds of arc (1 minute = 1/60th of a degree, 1 second = 1/3600th of a degree).\n"; + + os << "You can use the following form where R indicates radians, if you use --input-angle-measure radian:\n"; + os << "[+-]RRR.RRRRR"; +} + +void printConversion(const string &coordinates) +{ + if(coordinates.find(',') == string::npos && coordinates.find('N') == string::npos && coordinates.find('E') == string::npos) + cout << Angle(coordinates, inputAngularMeasure).toString(outputFormForAngles); + else + printLocation(locationFromString(coordinates)); +} + +void printDistance(const std::string &locationstr1, const std::string &locationstr2) +{ + printDistance(locationFromString(locationstr1) + .distanceTo(locationFromString(locationstr2))); +} + +void printDistance(double distance) +{ + if(distance > 1000) + cout << (distance / 1000.0) << " km"; + else + cout << distance << " m"; +} + +void printTrackLength(const string &filePath, bool circle) +{ + try { + vector locations(locationsFromFile(filePath)); + printDistance(Location::trackLength(locations, circle)); + cout << " (" << locations.size() << " trackpoints)"; + } catch(ios_base::failure &ex) { + cout << "An IO failure occured when reading file from provided path: " << ex.what() << endl; + } +} + +void printBearing(const string &locationstr1, const string &locationstr2) +{ + cout << locationFromString(locationstr1).initialBearingTo( + locationFromString(locationstr2)) + .toString(outputFormForAngles) << endl; +} + +void printFinalBearing(const string &locationstr1, const string &locationstr2) +{ + cout << locationFromString(locationstr1).finalBearingTo( + locationFromString(locationstr2)) + .toString(outputFormForAngles) << endl; +} + +void printMidpoint(const string &locationstr1, const string &locationstr2) +{ + printLocation(Location::midpoint( + locationFromString(locationstr1), + locationFromString(locationstr2))); +} + +void printDestination(const string &locationstr, const string &distancestr, const string &bearingstr) +{ + Location start = locationFromString(locationstr); + double distance = numberFromString(distancestr); + Angle bearing(bearingstr, inputAngularMeasure); + printLocation(start.destination(distance, bearing)); +} + +void printLocation(const Location &location) +{ + switch(outputSystemForLocations) { + case SystemForLocations::LatitudeLongitude: + cout << location.toString(outputFormForAngles); + break; + case SystemForLocations::UTMWGS84: + cout << location.toUtmWgs4String(); + break; + } +} + + +void printMapsLink(const string &filePath) +{ + try { + vector locations(locationsFromFile(filePath)); + if(locations.size() > 0) { + cout << "https://maps.google.de/maps?saddr="; + const Angle::OutputForm outputForm = Angle::OutputForm::Degrees; + cout << locations.front().toString(outputForm); + if(locations.size() > 1) { + cout << "&daddr="; + cout << locations.at(1).toString(outputForm); + } + if(locations.size() > 2) { + for(vector::const_iterator i = locations.cbegin() + 2, end = locations.cend(); i != end; ++i) { + cout << "+to:"; + cout << i->toString(outputForm); + } + } + cout << "&mra=mi&mrsp=2&sz=16&z=16"; + } else { + throw Failure("At least one location is required to generate a link."); + } + } catch(ios_base::failure &ex) { + cout << "An IO failure occured when reading file from provided path: " << ex.what() << endl; + } +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..c1a4d75 --- /dev/null +++ b/main.h @@ -0,0 +1,39 @@ +#ifndef MAIN_H_INCLUDED +#define MAIN_H_INCLUDED + +#include "angle.h" +#include "location.h" + +#include +#include +#include + +enum class SystemForLocations +{ + LatitudeLongitude, + UTMWGS84 +}; + +extern Angle::AngularMeasure inputAngularMeasure; +extern Angle::OutputForm outputFormForAngles; +extern SystemForLocations inputSystemForLocations; +extern SystemForLocations outputSystemForLocations; + +int main(int argc, char *argv[]); + +Location locationFromString(const std::string &userInput); +std::vector locationsFromFile(const std::string &path); +void printAngleFormatInfo(std::ostream &os); +void printConversion(const std::string &coordinates); +void printDistance(const std::string &locationstr1, const std::string &locationstr2); +void printDistance(double distance); +void printTrackLength(const std::string &filePath, bool circle = false); +void printBearing(const std::string &locationstr1, const std::string &locationstr2); +void printFinalBearing(const std::string &locationstr1, const std::string &locationstr2); +void printMidpoint(const std::string &locationstr1, const std::string &locationstr2); +void printDestination(const std::string &locationstr, const std::string &distancestr, const std::string &bearingstr); +void printLocation(const Location &location); +void printMapsLink(const std::string &filePath); + + +#endif // MAIN_H_INCLUDED diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..9bfbc42 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,6 @@ +#include "utils.h" + +namespace Utils +{ + +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..e406e6a --- /dev/null +++ b/utils.h @@ -0,0 +1,22 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +#include +#include + +namespace ConversionUtilities +{ + +template +T numberFromString(const std::string &value) { + T result; + std::stringstream ss(value, std::stringstream::in | std::stringstream::out); + if(ss >> result && ss.eof()) return result; + else throw ApplicationUtilities::Failure(value + " is no number"); +} + +} + +#endif // UTILS_H