diff --git a/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.swift b/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.swift
index dbe95c1a6f..3d857496f8 100644
--- a/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.swift
+++ b/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.swift
@@ -7,11 +7,11 @@ final class BookmarksListViewController: MWMViewController {
private var canEdit = false
@IBOutlet private var tableView: UITableView!
- @IBOutlet private var searchBar: UISearchBar!
@IBOutlet private var toolBar: UIToolbar!
@IBOutlet private var sortToolbarItem: UIBarButtonItem!
@IBOutlet private var moreToolbarItem: UIBarButtonItem!
-
+ private let searchController = UISearchController(searchResultsController: nil)
+
private lazy var infoViewController: BookmarksListInfoViewController = {
let infoViewController = BookmarksListInfoViewController()
infoViewController.delegate = self
@@ -30,7 +30,16 @@ final class BookmarksListViewController: MWMViewController {
sortToolbarItem.setTitleTextAttributes(toolbarItemAttributes, for: .normal)
moreToolbarItem.setTitleTextAttributes(toolbarItemAttributes, for: .normal)
sortToolbarItem.title = L("sort")
- searchBar.placeholder = L("search_in_the_list")
+
+ extendedLayoutIncludesOpaqueBars = true
+ searchController.searchBar.placeholder = L("search_in_the_list")
+ searchController.obscuresBackgroundDuringPresentation = false
+ searchController.hidesNavigationBarDuringPresentation = alternativeSizeClass(iPhone: true, iPad: false)
+ searchController.searchBar.delegate = self
+ searchController.searchBar.applyTheme()
+ navigationItem.searchController = searchController
+ navigationItem.hidesSearchBarWhenScrolling = false
+
cellStrategy.registerCells(tableView)
cellStrategy.cellCheckHandler = { [weak self] (viewModel, index, checked) in
self?.presenter.checkItem(in: viewModel, at: index, checked: checked)
@@ -157,13 +166,11 @@ extension BookmarksListViewController: UITableViewDelegate {
extension BookmarksListViewController: UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.setShowsCancelButton(true, animated: true)
- navigationController?.setNavigationBarHidden(true, animated: true)
presenter.activateSearch()
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchBar.setShowsCancelButton(false, animated: true)
- navigationController?.setNavigationBarHidden(false, animated: true)
presenter.deactivateSearch()
}
diff --git a/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.xib b/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.xib
index e7d1728495..ed2639b95d 100644
--- a/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.xib
+++ b/iphone/Maps/Bookmarks/BookmarksList/BookmarksListViewController.xib
@@ -11,7 +11,6 @@
-
@@ -23,23 +22,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -77,19 +59,13 @@
-
-
-
-
-
-
-
+
diff --git a/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift b/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift
index b4a26d3731..274af6795a 100644
--- a/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift
+++ b/iphone/Maps/Bookmarks/Categories/BMCView/BMCViewController.swift
@@ -34,6 +34,7 @@ final class BMCViewController: MWMViewController {
override func viewDidLoad() {
super.viewDidLoad()
+ view.styleName = "PressBackground"
viewModel = BMCDefaultViewModel()
}
diff --git a/iphone/Maps/Core/Theme/GlobalStyleSheet.swift b/iphone/Maps/Core/Theme/GlobalStyleSheet.swift
index 1ee1021bd6..2cba499355 100644
--- a/iphone/Maps/Core/Theme/GlobalStyleSheet.swift
+++ b/iphone/Maps/Core/Theme/GlobalStyleSheet.swift
@@ -30,8 +30,9 @@ class GlobalStyleSheet: IStyleSheet {
theme.add(styleName: "SearchBar") { (s) -> (Void) in
s.backgroundColor = colors.white
s.barTintColor = colors.primary
- s.tintColor = UIColor.white
- s.fontColor = colors.blackSecondaryText
+ s.fontColor = colors.blackPrimaryText
+ s.fontColorDetailed = UIColor.white
+ s.tintColor = colors.blackSecondaryText
}
theme.add(styleName: "NavigationBar") { (s) -> (Void) in
diff --git a/iphone/Maps/Core/Theme/Renderers/UISearchBarRenderer.swift b/iphone/Maps/Core/Theme/Renderers/UISearchBarRenderer.swift
index ada3393185..89182b2ce3 100644
--- a/iphone/Maps/Core/Theme/Renderers/UISearchBarRenderer.swift
+++ b/iphone/Maps/Core/Theme/Renderers/UISearchBarRenderer.swift
@@ -27,7 +27,23 @@ class UISearchBarRenderer: UIViewRenderer {
var searchTextField: UITextField?
if #available(iOS 13, *) {
searchTextField = control.searchTextField
+ control.searchTextField.layer.cornerCurve = .continuous
}
+
+ // Default search bar implementation adds the grey transparent image for background. This code removes it and updates the corner radius. This is not working on iPad designed for mac.
+ if #available(iOS 14.0, *), ProcessInfo.processInfo.isiOSAppOnMac {
+ } else {
+ control.setSearchFieldBackgroundImage(UIImage(), for: .normal)
+ }
+
+ searchTextField?.layer.cornerRadius = 8
+ searchTextField?.layer.masksToBounds = true
+
+ // Placeholder color
+ if let placeholder = searchTextField?.placeholder {
+ searchTextField?.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [.foregroundColor: UIColor.gray])
+ }
+
if let backgroundColor = style.backgroundColor {
searchTextField?.backgroundColor = backgroundColor
}
@@ -37,16 +53,28 @@ class UISearchBarRenderer: UIViewRenderer {
control.setBackgroundImage(barTintColor.getImage(), for: position, barMetrics: .default)
control.backgroundColor = barTintColor
}
- if let tintColor = style.tintColor {
- control.tintColor = tintColor
- }
if let font = style.font {
searchTextField?.font = font
}
if let fontColor = style.fontColor {
searchTextField?.textColor = fontColor
- searchTextField?.leftView?.tintColor = fontColor
- searchTextField?.tintColor = fontColor
+ }
+ if let fontColorDetailed = style.fontColorDetailed {
+ // Cancel button color
+ control.tintColor = fontColorDetailed
+ }
+ if let tintColor = style.tintColor {
+ searchTextField?.leftView?.tintColor = tintColor
+ // Placeholder indicator color
+ searchTextField?.tintColor = tintColor
+ // Clear button image
+ let clearButtonImage: UIImage?
+ if #available(iOS 13.0, *) {
+ clearButtonImage = UIImage(named: "ic_clear")?.withRenderingMode(.alwaysTemplate).withTintColor(tintColor)
+ } else {
+ clearButtonImage = UIImage(named: "ic_search_clear_14")
+ }
+ control.setImage(clearButtonImage, for: .clear, state: .normal)
}
}
diff --git a/iphone/Maps/Core/Theme/Renderers/UITextFieldRenderer.swift b/iphone/Maps/Core/Theme/Renderers/UITextFieldRenderer.swift
index 26f15d68d6..4868a46599 100644
--- a/iphone/Maps/Core/Theme/Renderers/UITextFieldRenderer.swift
+++ b/iphone/Maps/Core/Theme/Renderers/UITextFieldRenderer.swift
@@ -19,6 +19,13 @@ extension UITextField {
class UITextFieldRenderer {
class func render(_ control: UITextField, style: Style) {
+ if let cornerRadius = style.cornerRadius {
+ control.layer.cornerRadius = cornerRadius
+ control.clipsToBounds = true
+ if #available(iOS 13.0, *) {
+ control.layer.cornerCurve = .continuous
+ }
+ }
var placeholderAttributes = [NSAttributedString.Key : Any]()
if let backgroundColor = style.backgroundColor {
control.backgroundColor = backgroundColor
@@ -29,7 +36,6 @@ class UITextFieldRenderer {
}
if let fontColor = style.fontColor {
control.textColor = fontColor
-
}
if let tintColor = style.tintColor {
control.tintColor = tintColor
diff --git a/iphone/Maps/Core/Theme/SearchStyleSheet.swift b/iphone/Maps/Core/Theme/SearchStyleSheet.swift
index ab2fabe407..978d7d88fe 100644
--- a/iphone/Maps/Core/Theme/SearchStyleSheet.swift
+++ b/iphone/Maps/Core/Theme/SearchStyleSheet.swift
@@ -56,9 +56,11 @@ class SearchStyleSheet: IStyleSheet {
}
theme.add(styleName: "SearchSearchTextField") { (s) -> Void in
- s.fontColor = colors.blackSecondaryText
+ s.fontColor = colors.blackPrimaryText
s.backgroundColor = colors.white
s.tintColor = colors.blackSecondaryText
+ s.cornerRadius = 8.0
+ s.barTintColor = colors.primary
}
theme.add(styleName: "SearchSearchTextFieldIcon") { (s) -> Void in
diff --git a/iphone/Maps/Images.xcassets/Search/ic_clear.symbolset/Contents.json b/iphone/Maps/Images.xcassets/Search/ic_clear.symbolset/Contents.json
new file mode 100644
index 0000000000..0be0ab1451
--- /dev/null
+++ b/iphone/Maps/Images.xcassets/Search/ic_clear.symbolset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "symbols" : [
+ {
+ "filename" : "xmark.circle.fill.svg",
+ "idiom" : "universal"
+ }
+ ]
+}
diff --git a/iphone/Maps/Images.xcassets/Search/ic_clear.symbolset/xmark.circle.fill.svg b/iphone/Maps/Images.xcassets/Search/ic_clear.symbolset/xmark.circle.fill.svg
new file mode 100644
index 0000000000..620a552982
--- /dev/null
+++ b/iphone/Maps/Images.xcassets/Search/ic_clear.symbolset/xmark.circle.fill.svg
@@ -0,0 +1,197 @@
+
+
+
+
diff --git a/iphone/Maps/UI/Downloader/DownloadMapsViewController.swift b/iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
index e9e172c78b..8d6d169e93 100644
--- a/iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
+++ b/iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
@@ -27,7 +27,7 @@ class DownloadMapsViewController: MWMViewController {
// MARK: - Properties
- private var searchBar: UISearchBar = UISearchBar()
+ private var searchController = UISearchController(searchResultsController: nil)
var dataSource: IDownloaderDataSource!
@objc var mode: MWMMapDownloaderMode = .downloaded
private var skipCountryEvent = false
@@ -77,13 +77,15 @@ class DownloadMapsViewController: MWMViewController {
navigationItem.rightBarButtonItem = addMapsButton
}
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
-
- if dataSource.isRoot {
- searchBar.placeholder = L("downloader_search_field_hint")
- searchBar.delegate = self
- // TODO: Fix the height and centering of the searchBar, it's very tricky.
- navigationItem.titleView = searchBar
- }
+ extendedLayoutIncludesOpaqueBars = true
+ searchController.searchBar.placeholder = L("downloader_search_field_hint")
+ searchController.searchBar.delegate = self
+ searchController.obscuresBackgroundDuringPresentation = false
+ searchController.hidesNavigationBarDuringPresentation = alternativeSizeClass(iPhone: true, iPad: false)
+ searchController.searchBar.applyTheme()
+ navigationItem.searchController = searchController
+ navigationItem.hidesSearchBarWhenScrolling = false
+
configButtons()
}
@@ -279,7 +281,7 @@ extension DownloadMapsViewController: UITableViewDataSource {
cell = placeCell
}
cell.mode = dataSource.isSearching ? .available : mode
- cell.config(nodeAttrs, searchQuery: searchBar.text)
+ cell.config(nodeAttrs, searchQuery: searchController.searchBar.text)
cell.delegate = self
return cell
}
@@ -355,7 +357,7 @@ extension DownloadMapsViewController: UITableViewDelegate {
extension DownloadMapsViewController: UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
- searchBar.resignFirstResponder()
+ searchController.searchBar.resignFirstResponder()
}
}
@@ -412,7 +414,7 @@ extension DownloadMapsViewController: StorageObserver {
guard let downloaderCell = cell as? MWMMapDownloaderTableViewCell else { continue }
if downloaderCell.nodeAttrs.countryId != countryId { continue }
guard let indexPath = tableView.indexPath(for: downloaderCell) else { return }
- downloaderCell.config(dataSource.item(at: indexPath), searchQuery: searchBar.text)
+ downloaderCell.config(dataSource.item(at: indexPath), searchQuery: searchController.searchBar.text)
}
}
@@ -454,14 +456,6 @@ extension DownloadMapsViewController: UISearchBarDelegate {
}
}
-// MARK: - UIBarPositioningDelegate
-
-extension DownloadMapsViewController: UIBarPositioningDelegate {
- func position(for bar: UIBarPositioning) -> UIBarPosition {
- .topAttached
- }
-}
-
// MARK: - DownloadAllViewDelegate
extension DownloadMapsViewController: DownloadAllViewDelegate {
diff --git a/iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift b/iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
index 77d939e116..61534c524f 100644
--- a/iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
+++ b/iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
@@ -41,7 +41,7 @@ extension DownloadedMapsDataSource: IDownloaderDataSource {
var title: String {
guard let parentCountryId = parentCountryId else {
- return "" // Root Downloader dialog displays UISearchBar instead of title.
+ return L("download_maps")
}
return Storage.shared().name(forCountry: parentCountryId)
}
diff --git a/iphone/Maps/UI/Search/MWMSearchView.xib b/iphone/Maps/UI/Search/MWMSearchView.xib
index 5cdbe54275..336dffe9a4 100644
--- a/iphone/Maps/UI/Search/MWMSearchView.xib
+++ b/iphone/Maps/UI/Search/MWMSearchView.xib
@@ -1,9 +1,9 @@
-
+
-
+
@@ -19,7 +19,7 @@
-
+
@@ -35,10 +35,10 @@
-
-
+
+
-
+
@@ -51,12 +51,12 @@
-
+
@@ -79,7 +79,7 @@
-
+
@@ -88,7 +88,7 @@
@@ -158,7 +158,7 @@
-
+
@@ -227,7 +227,7 @@
-
+
diff --git a/iphone/Maps/UI/Search/SearchBar.swift b/iphone/Maps/UI/Search/SearchBar.swift
index 32997b038b..d7adb26937 100644
--- a/iphone/Maps/UI/Search/SearchBar.swift
+++ b/iphone/Maps/UI/Search/SearchBar.swift
@@ -29,12 +29,6 @@ final class SearchBar: SolidTouchView {
}
}
- private lazy var dateFormatter: DateFormatter = {
- let formatter = DateFormatter()
- formatter.setLocalizedDateFormatFromTemplate("EEE, MMMd")
- return formatter
- }()
-
override func awakeFromNib() {
super.awakeFromNib()
updateLeftView()
diff --git a/iphone/Maps/UI/Search/SearchTextField.swift b/iphone/Maps/UI/Search/SearchTextField.swift
index d600ba7c87..2f623249fd 100644
--- a/iphone/Maps/UI/Search/SearchTextField.swift
+++ b/iphone/Maps/UI/Search/SearchTextField.swift
@@ -17,7 +17,13 @@ class SearchTextField: UITextField {
for view in subviews {
if (view is UIButton) {
let button = view as? UIButton
- button?.setImage(UIImage(named: "ic_search_clear_14"), for: .normal)
+ let clearButtonImage: UIImage?
+ if #available(iOS 13.0, *) {
+ clearButtonImage = UIImage(named: "ic_clear")?.withRenderingMode(.alwaysTemplate).withTintColor(tintColor)
+ } else {
+ clearButtonImage = UIImage(named: "ic_search_clear_14")
+ }
+ button?.setImage(clearButtonImage, for: .normal)
button?.tintColor = tintColor
}
}
diff --git a/iphone/Maps/UI/Storyboard/Main.storyboard b/iphone/Maps/UI/Storyboard/Main.storyboard
index 220bcba5fb..16bebafb71 100644
--- a/iphone/Maps/UI/Storyboard/Main.storyboard
+++ b/iphone/Maps/UI/Storyboard/Main.storyboard
@@ -91,6 +91,7 @@
+
@@ -186,7 +187,6 @@
-
@@ -232,11 +232,11 @@
-
-
+
+
@@ -274,11 +274,11 @@
-
+
+
-
-
+
@@ -910,12 +910,12 @@
-
+
-
+
@@ -1000,32 +1000,11 @@
-
-
-
-
+
@@ -1042,15 +1021,36 @@
+
+
+
diff --git a/platform/utm_mgrs_utils.cpp b/platform/utm_mgrs_utils.cpp
index 1157ab3f57..28d250e8b9 100644
--- a/platform/utm_mgrs_utils.cpp
+++ b/platform/utm_mgrs_utils.cpp
@@ -8,55 +8,59 @@
namespace utm_mgrs_utils
{
-namespace
-{
+using namespace std;
+using namespace math; // To use constexpr math::pi
+using namespace base; // To use base::DegToRad and base::RadToDeg
+using namespace ms; // To use ms::LatLon
+
struct UTMPoint
{
double easting;
double northing;
- int zoneNumber;
- char zoneLetter;
+ int zone_number;
+ char zone_letter;
+
};
-double constexpr K0 = 0.9996; // The scale factor at the central meridian.
-double constexpr E = 0.00669438; // The square of the eccentricity for the WGS84 ellipsoid.
-double constexpr E2 = E * E;
-double constexpr E3 = E2 * E;
-double constexpr E_P2 = E / (1 - E);
-double constexpr R = 6378137.0; // The Earth equitorial radius for the WGS84 ellipsoid.
+constexpr double K0 = 0.9996; // The scale factor at the central meridian.
+constexpr double E = 0.00669438; // The square of the eccentricity for the WGS84 ellipsoid.
+constexpr double E2 = E * E;
+constexpr double E3 = E2 * E;
+constexpr double E_P2 = E / (1 - E);
+constexpr double R = 6378137.0; // The Earth equitorial radius for the WGS84 ellipsoid.
-double constexpr SQRT_E = 0.996647189; // sqrt(1 - E)
-double constexpr _E = (1 - SQRT_E) / (1 + SQRT_E);
-double constexpr _E2 = _E * _E;
-double constexpr _E3 = _E2 * _E;
-double constexpr _E4 = _E3 * _E;
-double constexpr _E5 = _E4 * _E;
+constexpr double SQRT_E = 0.996647189; // sqrt(1 - E)
+constexpr double _E = (1 - SQRT_E) / (1 + SQRT_E);
+constexpr double _E2 = _E * _E;
+constexpr double _E3 = _E2 * _E;
+constexpr double _E4 = _E3 * _E;
+constexpr double _E5 = _E4 * _E;
-double constexpr M1 = (1.0 - E / 4.0 - 3.0 * E2 / 64.0 - 5.0 * E3 / 256.0);
-double constexpr M2 = (3.0 * E / 8.0 + 3.0 * E2 / 32.0 + 45.0 * E3 / 1024.0);
-double constexpr M3 = (15.0 * E2 / 256.0 + 45.0 * E3 / 1024.0);
-double constexpr M4 = (35.0 * E3 / 3072.0);
+constexpr double M1 = (1.0 - E / 4.0 - 3.0 * E2 / 64.0 - 5.0 * E3 / 256.0);
+constexpr double M2 = (3.0 * E / 8.0 + 3.0 * E2 / 32.0 + 45.0 * E3 / 1024.0);
+constexpr double M3 = (15.0 * E2 / 256.0 + 45.0 * E3 / 1024.0);
+constexpr double M4 = (35.0 * E3 / 3072.0);
-double constexpr P2 = (3.0 / 2.0 * _E - 27.0 / 32.0 * _E3 + 269.0 / 512.0 * _E5);
-double constexpr P3 = (21.0 / 16.0 * _E2 - 55.0 / 32.0 * _E4);
-double constexpr P4 = (151.0 / 96.0 * _E3 - 417.0 / 128.0 * _E5);
-double constexpr P5 = (1097.0 / 512.0 * _E4);
+constexpr double P2 = (3.0 / 2.0 * _E - 27.0 / 32.0 * _E3 + 269.0 / 512.0 * _E5);
+constexpr double P3 = (21.0 / 16.0 * _E2 - 55.0 / 32.0 * _E4);
+constexpr double P4 = (151.0 / 96.0 * _E3 - 417.0 / 128.0 * _E5);
+constexpr double P5 = (1097.0 / 512.0 * _E4);
-int constexpr kInvalidEastingNorthing = -1;
+constexpr int kInvalidEastingNorthing = -1;
-std::string_view constexpr kZoneLetters = "CDEFGHJKLMNPQRSTUVWXX";
+const string ZONE_LETTERS = "CDEFGHJKLMNPQRSTUVWXX";
-std::string_view constexpr kSetOriginColumnLetters = "SAJSAJ";
-std::string_view constexpr kSetOriginRowLetters = "FAFAFA";
+const int NUM_100K_SETS = 6;
+const int SET_ORIGIN_COLUMN_LETTERS[] = { 'S', 'A', 'J', 'S', 'A', 'J' };
+const int SET_ORIGIN_ROW_LETTERS[] = { 'F', 'A', 'F', 'A', 'F', 'A' };
// Returns angle in radians to be between -π and π.
double NormalizeAngle(double value)
{
- using math::pi;
if (value < -pi)
- value += - 2 * pi * (static_cast(value - pi) / (2 * pi));
+ value += - 2 * pi * ((int)(value - pi) / (2 * pi));
else if (value > pi)
- value -= 2 * pi * (static_cast(value + pi) / (2 * pi));
+ value -= 2 * pi * ((int)(value + pi) / (2 * pi));
return value;
}
@@ -69,101 +73,97 @@ int LatLonToZoneNumber(double lat, double lon)
{
if (lon < 9.0)
return 31;
- if (lon < 21.0)
+ else if (lon < 21.0)
return 33;
- if (lon < 33.0)
+ else if (lon < 33.0)
return 35;
- if (lon < 42.0)
+ else if (lon < 42.0)
return 37;
}
- return static_cast((lon + 180.0) / 6.0) + 1;
+ return int((lon + 180.0) / 6.0) + 1;
}
-std::optional LatitudeToZoneLetter(double lat)
+optional LatitudeToZoneLetter(double lat)
{
if (-80.0 <= lat && lat <= 84.0)
- {
- auto const index = static_cast(lat + 80.0) >> 3;
- ASSERT_LESS(index, kZoneLetters.size(), ());
- return kZoneLetters[index];
- }
- return {};
+ return ZONE_LETTERS[int(lat + 80.0) >> 3];
+ else
+ return nullopt;
}
-int ZoneNumberToCentralLon(int zoneNumber)
+int ZoneNumberToCentralLon(int zone_number)
{
- return (zoneNumber - 1) * 6 - 180 + 3;
+ return (zone_number - 1) * 6 - 180 + 3;
}
-// Main algorithm. Formulas source: https://github.com/Turbo87/utm
+// Main algorithm. Formulars source: https://github.com/Turbo87/utm
UTMPoint LatLonToUtm(double lat, double lon)
{
- using std::sin, std::cos, std::sqrt;
+ double const lat_rad = DegToRad(lat);
+ double const lat_sin = sin(lat_rad);
+ double const lat_cos = cos(lat_rad);
- double const latRad = base::DegToRad(lat);
- double const latSin = sin(latRad);
- double const latCos = cos(latRad);
+ double const lat_tan = lat_sin / lat_cos;
+ double const lat_tan2 = lat_tan * lat_tan;
+ double const lat_tan4 = lat_tan2 * lat_tan2;
- double const latTan = latSin / latCos;
- double const latTan2 = latTan * latTan;
- double const latTan4 = latTan2 * latTan2;
+ int const zone_number = LatLonToZoneNumber(lat, lon);
+ char const zone_letter = (lat >= 0.0) ? 'N' : 'S';
- int const zoneNumber = LatLonToZoneNumber(lat, lon);
- char const zoneLetter = (lat >= 0.0) ? 'N' : 'S';
+ double const lon_rad = DegToRad(lon);
+ double const central_lon = ZoneNumberToCentralLon(zone_number);
+ double const central_lon_rad = DegToRad(central_lon);
- double const lonRad = base::DegToRad(lon);
- double const centralLon = ZoneNumberToCentralLon(zoneNumber);
- double const centralLonRad = base::DegToRad(centralLon);
+ double const n = R / sqrt(1 - E * lat_sin * lat_sin);
+ double const c = E_P2 * lat_cos * lat_cos;
- double const n = R / sqrt(1.0 - E * latSin * latSin);
- double const c = E_P2 * latCos * latCos;
-
- double const a = latCos * NormalizeAngle(lonRad - centralLonRad);
+ double const a = lat_cos * NormalizeAngle(lon_rad - central_lon_rad);
double const a2 = a * a;
double const a3 = a2 * a;
double const a4 = a3 * a;
double const a5 = a4 * a;
double const a6 = a5 * a;
- double const m = R * (M1 * latRad -
- M2 * sin(2 * latRad) +
- M3 * sin(4 * latRad) -
- M4 * sin(6 * latRad));
+ double const m = R * (M1 * lat_rad -
+ M2 * sin(2 * lat_rad) +
+ M3 * sin(4 * lat_rad) -
+ M4 * sin(6 * lat_rad));
double const easting = K0 * n * (a +
- a3 / 6 * (1 - latTan2 + c) +
- a5 / 120 * (5 - 18 * latTan2 + latTan4 + 72 * c - 58 * E_P2)) + 500000.0;
+ a3 / 6 * (1 - lat_tan2 + c) +
+ a5 / 120 * (5 - 18 * lat_tan2 + lat_tan4 + 72 * c - 58 * E_P2)) + 500000.0;
- double northing = K0 * (m + n * latTan * (a2 / 2 +
- a4 / 24 * (5 - latTan2 + 9 * c + 4 * c * c) +
- a6 / 720 * (61 - 58 * latTan2 + latTan4 + 600 * c - 330 * E_P2)));
+ double northing = K0 * (m + n * lat_tan * (a2 / 2 +
+ a4 / 24 * (5 - lat_tan2 + 9 * c + 4 * c * c) +
+ a6 / 720 * (61 - 58 * lat_tan2 + lat_tan4 + 600 * c - 330 * E_P2)));
if (lat < 0.0)
northing += 10000000.0;
- return {easting, northing, zoneNumber, zoneLetter};
+ return {easting, northing, zone_number, zone_letter};
}
// Generate UTM string from UTM point parameters.
-std::string UTMtoStr(UTMPoint const & point)
+string UTMtoStr(UTMPoint point)
{
// Easting and northing are rounded to nearest integer. Because of that in some cases
// last 5 digits of UTM and MGRS coordinates could differ (inaccuracy is no more then 1 meter).
// Some UTM converters truncate easting and northing instead of rounding. Consider this option.
- return std::to_string(point.zoneNumber) + point.zoneLetter + ' ' +
- std::to_string(static_cast(std::round(point.easting))) + ' ' +
- std::to_string(static_cast(std::round(point.northing)));
+
+ return to_string(point.zone_number) + string(1, point.zone_letter) + " " + \
+ to_string(int(round(point.easting))) + " " + \
+ to_string(int(round(point.northing)));
}
// Build 2 chars string with MGRS 100k designator.
-std::string Get100kId(double easting, double northing, int zoneNumber)
+string get_100k_id(double easting, double northing, int zone_number)
{
- int const set = zoneNumber % kSetOriginColumnLetters.size();
- int const setColumn = easting / 100000;
- int const setRow = static_cast(northing / 100000) % 20;
+ int set = zone_number % NUM_100K_SETS;
+ int setColumn = ((int) easting / 100000);
+ int setRow = ((int) northing / 100000) % 20;
- int const colOrigin = kSetOriginColumnLetters[set];
- int const rowOrigin = kSetOriginRowLetters[set];
+ int colOrigin = SET_ORIGIN_COLUMN_LETTERS[set];
+ int rowOrigin = SET_ORIGIN_ROW_LETTERS[set];
int colInt = colOrigin + setColumn - 1;
int rowInt = rowOrigin + setRow;
@@ -209,48 +209,52 @@ std::string Get100kId(double easting, double northing, int zoneNumber)
if (rowInt > 'V')
rowInt = rowInt - 'V' + 'A' - 1;
- return {static_cast(colInt), static_cast(rowInt)};
+ string twoLetter = {char(colInt), char(rowInt)};
+
+ return twoLetter;
}
// Convert UTM point parameters to MGRS parameters. Additional 2 char code is deducted.
// Easting and northing parameters are reduced to 5 digits.
-std::string UTMtoMgrsStr(UTMPoint const & point, int precision)
+string UTMtoMgrsStr(UTMPoint point, int precision)
{
- if (point.zoneLetter == 'Z')
+ if (point.zone_letter == 'Z')
return "Latitude limit exceeded";
- auto const eastingStr = strings::to_string_width(static_cast(point.easting), precision + 1);
- auto northingStr = strings::to_string_width(static_cast(point.northing), precision + 1);
+ else
+ {
+ string eastingStr = strings::to_string_width((long)point.easting, precision+1);
+ string northingStr = strings::to_string_width((long)point.northing, precision+1);
- if (northingStr.size() > 6)
- northingStr = northingStr.substr(northingStr.size() - 6);
+ if (northingStr.size() > 6)
+ northingStr = northingStr.substr(northingStr.size() - 6);
- return strings::to_string_width(point.zoneNumber, 2) + point.zoneLetter + ' ' +
- Get100kId(point.easting, point.northing, point.zoneNumber) + ' ' +
- eastingStr.substr(1, precision) + ' ' +
- northingStr.substr(1, precision);
+ return strings::to_string_width((long) point.zone_number, 2) + point.zone_letter + " " + \
+ get_100k_id(point.easting, point.northing, point.zone_number) + " " + \
+ eastingStr.substr(1, precision) + " " + \
+ northingStr.substr(1, precision);
+ }
}
-} // namespace
// Convert UTM parameters to lat,lon for WSG 84 ellipsoid.
// If UTM parameters are valid lat and lon references are used to output calculated coordinates.
-// Otherwise function returns empty optional.
-std::optional UTMtoLatLon(int easting, int northing, int zoneNumber, char zoneLetter)
+// Otherwise function returns 'false'.
+std::optional UTMtoLatLon(int easting, int northing, int zone_number, char zone_letter)
{
- if (zoneNumber < 1 || zoneNumber > 60)
- return {};
+ if (zone_number < 1 || zone_number > 60)
+ return nullopt;
if (easting < 100000 || easting >= 1000000)
- return {};
+ return nullopt;
if (northing < 0 || northing > 10000000)
- return {};
+ return nullopt;
- if (zoneLetter < 'C' || zoneLetter > 'X' || zoneLetter == 'I' || zoneLetter == 'O')
- return {};
+ if (zone_letter<'C' || zone_letter>'X' || zone_letter == 'I' || zone_letter == 'O')
+ return nullopt;
- bool const northern = (zoneLetter >= 'N');
- double const x = easting - 500000.0;
- double y = northing;
+ bool northern = (zone_letter >= 'N');
+ double x = (double)easting - 500000.0;
+ double y = (double)northing;
if (!northern)
y -= 10000000.0;
@@ -298,20 +302,17 @@ std::optional UTMtoLatLon(int easting, int northing, int zoneNumber,
d3 / 6.0 * (1.0 + 2.0 * p_tan2 + c) +
d5 / 120.0 * (5.0 - 2.0 * c + 28.0 * p_tan2 - 3.0 * c2 + 8.0 * E_P2 + 24.0 * p_tan4)) / p_cos;
- longitude = NormalizeAngle(longitude + base::DegToRad(static_cast(ZoneNumberToCentralLon(zoneNumber))));
+ longitude = NormalizeAngle(longitude + DegToRad((double)ZoneNumberToCentralLon(zone_number)));
- return ms::LatLon(base::RadToDeg(latitude), base::RadToDeg(longitude));
+ return LatLon(RadToDeg(latitude), RadToDeg(longitude));
}
-namespace
-{
-// Given the first letter from a two-letter MGRS 100k zone, and given the
-// MGRS table set for the zone number, figure out the easting value that
-// should be added to the other, secondary easting value.
-int SquareCharToEasting(char e, size_t set)
-{
- ASSERT_LESS(set, kSetOriginColumnLetters.size(), ());
- int curCol = kSetOriginColumnLetters[set];
+
+/* Given the first letter from a two-letter MGRS 100k zone, and given the
+ * MGRS table set for the zone number, figure out the easting value that
+ * should be added to the other, secondary easting value.*/
+int SquareCharToEasting(char e, int set) {
+ int curCol = SET_ORIGIN_COLUMN_LETTERS[set];
int eastingValue = 100000;
bool rewindMarker = false;
@@ -349,7 +350,7 @@ int SquareCharToNorthing(char n, int set)
if (n > 'V')
return kInvalidEastingNorthing;
- int curRow = kSetOriginRowLetters[set];
+ int curRow = SET_ORIGIN_ROW_LETTERS[set];
int northingValue = 0;
bool rewindMarker = false;
@@ -377,62 +378,105 @@ int SquareCharToNorthing(char n, int set)
// Get minimum northing value of a MGRS zone.
int ZoneToMinNorthing(char zoneLetter)
{
+ int northing;
switch (zoneLetter)
{
- case 'C': return 1100000;
- case 'D': return 2000000;
- case 'E': return 2800000;
- case 'F': return 3700000;
- case 'G': return 4600000;
- case 'H': return 5500000;
- case 'J': return 6400000;
- case 'K': return 7300000;
- case 'L': return 8200000;
- case 'M': return 9100000;
- case 'N': return 0;
- case 'P': return 800000;
- case 'Q': return 1700000;
- case 'R': return 2600000;
- case 'S': return 3500000;
- case 'T': return 4400000;
- case 'U': return 5300000;
- case 'V': return 6200000;
- case 'W': return 7000000;
- case 'X': return 7900000;
- default: return kInvalidEastingNorthing;
+ case 'C':
+ northing = 1100000;
+ break;
+ case 'D':
+ northing = 2000000;
+ break;
+ case 'E':
+ northing = 2800000;
+ break;
+ case 'F':
+ northing = 3700000;
+ break;
+ case 'G':
+ northing = 4600000;
+ break;
+ case 'H':
+ northing = 5500000;
+ break;
+ case 'J':
+ northing = 6400000;
+ break;
+ case 'K':
+ northing = 7300000;
+ break;
+ case 'L':
+ northing = 8200000;
+ break;
+ case 'M':
+ northing = 9100000;
+ break;
+ case 'N':
+ northing = 0;
+ break;
+ case 'P':
+ northing = 800000;
+ break;
+ case 'Q':
+ northing = 1700000;
+ break;
+ case 'R':
+ northing = 2600000;
+ break;
+ case 'S':
+ northing = 3500000;
+ break;
+ case 'T':
+ northing = 4400000;
+ break;
+ case 'U':
+ northing = 5300000;
+ break;
+ case 'V':
+ northing = 6200000;
+ break;
+ case 'W':
+ northing = 7000000;
+ break;
+ case 'X':
+ northing = 7900000;
+ break;
+ default:
+ northing = kInvalidEastingNorthing;
}
+
+ return northing;
}
-} // namespace
// Convert MGRS parameters to UTM parameters and then use UTM to lat,lon conversion.
-std::optional MGRStoLatLon(int easting, int northing, int zoneCode, char zoneLetter, char squareCode[2])
+std::optional MGRStoLatLon(int easting, int northing, int zone_code, char zone_letter, char square_code[2])
{
// Convert easting and northing according to zone_code and square_code
- if (zoneCode < 1 || zoneCode > 60)
- return {};
+ if (zone_code < 1 || zone_code > 60)
+ return nullopt;
- if (zoneLetter <= 'B' || zoneLetter >= 'Y' || zoneLetter == 'I' || zoneLetter == 'O')
- return {};
+ if (zone_letter <= 'B' || zone_letter >= 'Y' || zone_letter == 'I' || zone_letter == 'O')
+ return nullopt;
- auto const set = zoneCode % kSetOriginColumnLetters.size();
+ int set = zone_code % NUM_100K_SETS;
- auto const char1 = squareCode[0];
- auto const char2 = squareCode[1];
+ char char1 = square_code[0];
+ char char2 = square_code[1];
if (char1 < 'A' || char2 < 'A' || char1 > 'Z' || char2 > 'Z' || char1 == 'I' || char2 == 'I' || char1 == 'O' || char2 == 'O')
- return {};
+ return nullopt;
- int const east100k = SquareCharToEasting(char1, set);
+ int east100k = SquareCharToEasting(char1, set);
if (east100k == kInvalidEastingNorthing)
- return {};
+ return nullopt;
int north100k = SquareCharToNorthing(char2, set);
if (north100k == kInvalidEastingNorthing)
- return {};
+ return nullopt;
- int const minNorthing = ZoneToMinNorthing(zoneLetter);
+ int minNorthing = ZoneToMinNorthing(zone_letter);
if (minNorthing == kInvalidEastingNorthing)
- return {};
+ return nullopt;
while (north100k < minNorthing)
north100k += 2000000;
@@ -440,11 +484,11 @@ std::optional MGRStoLatLon(int easting, int northing, int zoneCode,
easting += east100k;
northing += north100k;
- return UTMtoLatLon(easting, northing, zoneCode, zoneLetter);
+ return UTMtoLatLon(easting, northing, zone_code, zone_letter);
}
// Convert lat,lon for WGS84 ellipsoid to MGRS string.
-std::string FormatMGRS(double lat, double lon, int precision)
+string FormatMGRS(double lat, double lon, int precision)
{
if (precision > 5)
precision = 5;
@@ -459,10 +503,10 @@ std::string FormatMGRS(double lat, double lon, int precision)
UTMPoint mgrsp = LatLonToUtm(lat, lon);
// Need to set the right letter for the latitude.
- auto const maybeZone = LatitudeToZoneLetter(lat);
+ auto maybeZone = LatitudeToZoneLetter(lat);
if (maybeZone)
{
- mgrsp.zoneLetter = *maybeZone;
+ mgrsp.zone_letter = maybeZone.value();
return UTMtoMgrsStr(mgrsp, precision);
}
@@ -470,14 +514,16 @@ std::string FormatMGRS(double lat, double lon, int precision)
}
// Convert lat,lon for WGS84 ellipsoid to UTM string.
-std::string FormatUTM(double lat, double lon)
+string FormatUTM(double lat, double lon)
{
if (lat <= -80 || lat > 84)
return {}; // Latitude limit exceeded.
if (lon <= -180 || lon > 180)
return {}; // Longitude limit exceeded.
- return UTMtoStr(LatLonToUtm(lat, lon));
+ UTMPoint utm = LatLonToUtm(lat, lon);
+ return UTMtoStr(utm);
}
+
} // namespace utm_mgrs_utils
diff --git a/platform/utm_mgrs_utils.hpp b/platform/utm_mgrs_utils.hpp
index 48a2cdf406..0598095e86 100644
--- a/platform/utm_mgrs_utils.hpp
+++ b/platform/utm_mgrs_utils.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include
#include
#include "geometry/latlon.hpp"
@@ -38,6 +37,6 @@ std::string FormatMGRS(double lat, double lon, int prec);
std::optional UTMtoLatLon(int easting, int northing, int zone_code, char zone_letter);
// Covevrt MGRS coordinates to Lat Lon. If parameters are invalid function returns false
-std::optional MGRStoLatLon(int easting, int northing, int zoneCode, char zone_letter, char squareCode[2]);
+std::optional MGRStoLatLon(int easting, int northing, int zone_code, char zone_letter, char square_code[2]);
} // namespace utm_mgrs_utils
diff --git a/search/search_tests/utm_mgrs_coords_match_test.cpp b/search/search_tests/utm_mgrs_coords_match_test.cpp
index 49ba78f6f1..7a7894fac1 100644
--- a/search/search_tests/utm_mgrs_coords_match_test.cpp
+++ b/search/search_tests/utm_mgrs_coords_match_test.cpp
@@ -4,16 +4,18 @@
#include "base/math.hpp"
-#include
+#include
namespace utm_mgrs_coords_match_test
{
-void TestAlmostEqual(std::optional const & maybeLatLon, double expectedLat, double expectedLon)
-{
- TEST(maybeLatLon, ());
+using namespace search;
- // We expect the results to be quite precise.
- static double constexpr kEps = 1e-5;
+// We expect the results to be quite precise.
+double const kEps = 1e-5;
+
+void TestAlmostEqual(std::optional maybeLatLon, double expectedLat, double expectedLon)
+{
+ TEST(maybeLatLon.has_value(), ());
auto const actualLat = maybeLatLon->m_lat;
TEST(base::AlmostEqualAbsOrRel(actualLat, expectedLat, kEps), ("Lat is not close", actualLat, expectedLat));
@@ -24,14 +26,9 @@ void TestAlmostEqual(std::optional const & maybeLatLon, double expec
UNIT_TEST(MatchUTMCoords)
{
- using search::MatchUTMCoords;
-
- TEST(!MatchUTMCoords(" "), ());
-
// Extra spaces shouldn't break format
- TEST(MatchUTMCoords("15 N 500000 4649776"), ());
- TEST(MatchUTMCoords("15 N 500000 4649776"), ());
- TEST(MatchUTMCoords("15 N 500000 4649776"), ());
+ TEST(MatchUTMCoords("15 N 500000 4649776").has_value(), ());
+ TEST(MatchUTMCoords("15 N 500000 4649776").has_value(), ());
TestAlmostEqual(MatchUTMCoords("15N 500000 4649776"), 42.0, -93.0);
TestAlmostEqual(MatchUTMCoords("15 N 500000 4649776"), 42.0, -93.0);
@@ -43,81 +40,75 @@ UNIT_TEST(MatchUTMCoords)
UNIT_TEST(MatchUTMCoords_False)
{
- using search::MatchUTMCoords;
-
- TEST(!MatchUTMCoords("2 1st"), ());
- TEST(!MatchUTMCoords("15N5000004649776"), ());
+ TEST(!MatchUTMCoords("2 1st").has_value(), ());
+ TEST(!MatchUTMCoords("15N5000004649776").has_value(), ());
// Wrong zone number (first two digits)
- TEST(!MatchUTMCoords("0X 476594 9328501"), ());
- TEST(!MatchUTMCoords("0 X 476594 9328501"), ());
- TEST(!MatchUTMCoords("61N 294409 5628898"), ());
- TEST(!MatchUTMCoords("61 N 294409 5628898"), ());
+ TEST(!MatchUTMCoords("0X 476594 9328501").has_value(), ());
+ TEST(!MatchUTMCoords("0 X 476594 9328501").has_value(), ());
+ TEST(!MatchUTMCoords("61N 294409 5628898").has_value(), ());
+ TEST(!MatchUTMCoords("61 N 294409 5628898").has_value(), ());
// Wrong zone letter
- TEST(!MatchUTMCoords("25I 500000 4649776"), ());
- TEST(!MatchUTMCoords("25 I 500000 4649776"), ());
- TEST(!MatchUTMCoords("25O 500000 4649776"), ());
- TEST(!MatchUTMCoords("25 O 500000 4649776"), ());
- TEST(!MatchUTMCoords("5A 500000 4649776"), ());
- TEST(!MatchUTMCoords("5 A 500000 4649776"), ());
- TEST(!MatchUTMCoords("7B 500000 4649776"), ());
- TEST(!MatchUTMCoords("7 B 500000 4649776"), ());
+ TEST(!MatchUTMCoords("25I 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("25 I 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("25O 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("25 O 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("5A 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("5 A 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("7B 500000 4649776").has_value(), ());
+ TEST(!MatchUTMCoords("7 B 500000 4649776").has_value(), ());
// easting out of range (must be between 100,000 m and 999,999 m)
- TEST(!MatchUTMCoords("19S 999 6360877"), ());
- TEST(!MatchUTMCoords("19S 99999 6360877"), ());
- TEST(!MatchUTMCoords("19S 1000000 6360877"), ());
- TEST(!MatchUTMCoords("19S 2000000 6360877"), ());
+ TEST(!MatchUTMCoords("19S 999 6360877").has_value(), ());
+ TEST(!MatchUTMCoords("19S 99999 6360877").has_value(), ());
+ TEST(!MatchUTMCoords("19S 1000000 6360877").has_value(), ());
+ TEST(!MatchUTMCoords("19S 2000000 6360877").has_value(), ());
// northing out of range (must be between 0 m and 10,000,000 m)
- TEST(!MatchUTMCoords("30N 476594 10000001"), ());
- TEST(!MatchUTMCoords("30N 476594 20000000"), ());
+ TEST(!MatchUTMCoords("30N 476594 10000001").has_value(), ());
+ TEST(!MatchUTMCoords("30N 476594 20000000").has_value(), ());
}
UNIT_TEST(MatchMGRSCoords_parsing)
{
- using search::MatchMGRSCoords;
-
- TEST(MatchMGRSCoords("30N YF 67993 00000"), ());
- TEST(MatchMGRSCoords("30N YF 67993 00000 "), ());
- TEST(MatchMGRSCoords("30N YF 67993 00000 "), ());
- TEST(MatchMGRSCoords("30N YF 67993 00000 "), ());
- TEST(MatchMGRSCoords("30NYF 67993 00000"), ());
- TEST(MatchMGRSCoords("30NYF 67993 00000 "), ());
- TEST(MatchMGRSCoords("30NYF 67993 00000 "), ());
- TEST(MatchMGRSCoords("30NYF67993 00000"), ());
- TEST(MatchMGRSCoords("30NYF67993 00000 "), ());
- TEST(MatchMGRSCoords("30NYF67993 00000 "), ());
- TEST(MatchMGRSCoords("30NYF6799300000"), ());
- TEST(MatchMGRSCoords("30NYF6799300000 "), ());
+ TEST(MatchMGRSCoords("30N YF 67993 00000").has_value(), ());
+ TEST(MatchMGRSCoords("30N YF 67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30N YF 67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30N YF 67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF 67993 00000").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF 67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF 67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF67993 00000").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF67993 00000 ").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF6799300000").has_value(), ());
+ TEST(MatchMGRSCoords("30NYF6799300000 ").has_value(), ());
// Wrong number of digits
- TEST(!MatchMGRSCoords("30NYF 679930000 "), ());
- TEST(!MatchMGRSCoords("30NYF 679930000"), ());
+ TEST(!MatchMGRSCoords("30NYF 679930000 ").has_value(), ());
+ TEST(!MatchMGRSCoords("30NYF 679930000").has_value(), ());
- TEST(!MatchMGRSCoords("30N YF 693 23020"), ());
- TEST(!MatchMGRSCoords("30N YF 693 23 "), ());
+ TEST(!MatchMGRSCoords("30N YF 693 23020").has_value(), ());
+ TEST(!MatchMGRSCoords("30N YF 693 23 ").has_value(), ());
// Invalid zone
- TEST(!MatchMGRSCoords("30 FF 693 230"), ());
- TEST(!MatchMGRSCoords("30A YF 693 230"), ());
- TEST(!MatchMGRSCoords("30Z YF 693 230"), ());
- TEST(!MatchMGRSCoords("30Z F 693 230"), ());
- TEST(!MatchMGRSCoords("30Z 3F 693 230"), ());
- TEST(!MatchMGRSCoords("30Z K? 693 230"), ());
- TEST(!MatchMGRSCoords("30Z IB 693 230"), ());
- TEST(!MatchMGRSCoords("30Z DO 693 230"), ());
+ TEST(!MatchMGRSCoords("30 FF 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30A YF 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30Z YF 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30Z F 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30Z 3F 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30Z K? 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30Z IB 693 230").has_value(), ());
+ TEST(!MatchMGRSCoords("30Z DO 693 230").has_value(), ());
// Wrong easting or northing
- TEST(!MatchMGRSCoords("30NYF 679_3 00000"), ());
- TEST(!MatchMGRSCoords("30NYF 6930&000"), ());
+ TEST(!MatchMGRSCoords("30NYF 679_3 00000").has_value(), ());
+ TEST(!MatchMGRSCoords("30NYF 6930&000").has_value(), ());
}
UNIT_TEST(MatchMGRSCoords_convert)
{
- using search::MatchMGRSCoords;
-
TestAlmostEqual(MatchMGRSCoords("30N YF 67993 00000"), 0.000000, -0.592330);
TestAlmostEqual(MatchMGRSCoords("31N BA 00000 00000"), 0.000000, 0.304980);
TestAlmostEqual(MatchMGRSCoords("32R LR 00000 00000"), 27.107980, 6.982490);
@@ -159,6 +150,7 @@ UNIT_TEST(MatchMGRSCoords_convert)
TestAlmostEqual(MatchMGRSCoords("24P YV 49519 98153"), 13.541120, -36.694510);
TestAlmostEqual(MatchMGRSCoords("31K BA 64726 02675"), -18.051740, 0.777360);
TestAlmostEqual(MatchMGRSCoords("31N BA 32007 00000"), 0.000000, 0.592330);
+
}
} // namespace utm_mgrs_coords_match_test
diff --git a/search/utm_mgrs_coords_match.cpp b/search/utm_mgrs_coords_match.cpp
index 2535feb2f6..36d1972d6a 100644
--- a/search/utm_mgrs_coords_match.cpp
+++ b/search/utm_mgrs_coords_match.cpp
@@ -4,67 +4,74 @@
#include "platform/utm_mgrs_utils.hpp"
+#include
#include
+#include
+#include
#include "base/math.hpp"
#include "base/string_utils.hpp"
namespace search
{
-static std::string_view constexpr kSpaceChars = " \t\r\n";
+
+using namespace std;
+
+string const kSpaceChars = " \t\r";
+
// Matches 2 digits zone code. Returns end position of matched chars or string::npos if no match.
-static size_t MatchZoneCode(std::string const & query, int & zoneCode)
+size_t MatchZoneCode(string const & query, int & zone_code)
{
auto const pos = query.find_first_not_of(kSpaceChars);
- if (pos == std::string::npos || query.size() < pos + 2)
- return std::string::npos;
+ if (query.size() - pos < 2)
+ return string::npos;
char const dig1 = query[pos];
- char const dig2 = query[pos + 1];
- if (dig1 > '9' || dig1 < '0' || dig2 > '9' || dig2 < '0')
- return std::string::npos;
+ char const dig2 = query[pos+1];
+ if (dig1 < '0' || dig1 > '9' || dig2 < '0' || dig2 > '9')
+ return string::npos;
- zoneCode = (dig1 - '0') * 10 + (dig2 - '0');
+ zone_code = (dig1 - '0') * 10 + (dig2 - '0');
return pos + 2;
}
// Matches zone letter ignoring spaces. Returns end position of matched chars or string::npos if no match.
-size_t MatchZoneLetter(std::string const & query, char & zoneLetter, size_t startPos)
+size_t MatchZoneLetter(string const & query, char & zone_letter, size_t startPos)
{
auto const pos = query.find_first_not_of(kSpaceChars, startPos);
- if (pos == std::string::npos || query.size() == pos)
- return std::string::npos;
+ if (query.size() == pos)
+ return string::npos;
char const l = query[pos];
if (l < 'A' || l > 'Z')
- return std::string::npos;
+ return string::npos;
- zoneLetter = l;
+ zone_letter = l;
return pos + 1;
}
// Matches long number ignoring spaces. Returns end position of matched chars or string::npos if no match.
-size_t MatchInt(std::string const & query, int & value, size_t startPos)
+size_t MatchInt(string const & query, int & value, size_t startPos)
{
auto pos = query.find_first_not_of(kSpaceChars, startPos);
- if (pos == std::string::npos || query.size() == pos)
- return std::string::npos;
+ if (query.size() == pos)
+ return string::npos;
int n = 0;
while (pos < query.size())
{
- char const ch = query[pos];
+ char ch = query[pos];
if (ch >= '0' && ch <= '9') // Found digit
{
n = n * 10 + (ch - '0');
- pos++;
+ pos ++;
}
- else if (kSpaceChars.find(ch) != std::string::npos) // Found space char matching end of the number
+ else if (kSpaceChars.find(ch) != string::npos) // Found space char matching end of the number
break;
else // Found invalid char
- return std::string::npos;
+ return string::npos;
}
value = n;
@@ -75,30 +82,29 @@ size_t MatchInt(std::string const & query, int & value, size_t startPos)
// Return true if parsed successfully or false otherwise.
// See utm_mgrs_coords_match_test.cpp for sample UTM strings
// TODO: Add support of Polar regions. E.g. "A 1492875 2040624"
-// TODO: Support additional formats listed here: https://www.killetsoft.de/t_0901_e.htm
-std::optional MatchUTMCoords(std::string const & query)
+std::optional MatchUTMCoords(string const & query)
{
int easting, northing;
- int zoneCode;
- char zoneLetter;
+ int zone_code;
+ char zone_letter;
- size_t pos = MatchZoneCode(query, zoneCode);
- if (pos == std::string::npos)
- return {};
+ size_t pos = MatchZoneCode(query, zone_code);
+ if (pos == string::npos)
+ return nullopt;
- pos = MatchZoneLetter(query, zoneLetter, pos);
- if (pos == std::string::npos)
- return {};
+ pos = MatchZoneLetter(query, zone_letter, pos);
+ if (pos == string::npos)
+ return nullopt;
pos = MatchInt(query, easting, pos);
- if (pos == std::string::npos)
- return {};
+ if (pos == string::npos)
+ return nullopt;
pos = MatchInt(query, northing, pos);
- if (pos == std::string::npos)
- return {};
+ if (pos == string::npos)
+ return nullopt;
- return utm_mgrs_utils::UTMtoLatLon(easting, northing, zoneCode, zoneLetter);
+ return utm_mgrs_utils::UTMtoLatLon(easting, northing, zone_code, zone_letter);
}
// Parse MGRS format "(\d\d\W)\s*(\W\W)\s*(\d+)\s*(\d+)" and converts it to lat,lon.
@@ -107,17 +113,17 @@ std::optional MatchUTMCoords(std::string const & query)
// TODO: Add support of Polar regions. E.g. "A SN 92875 40624"
std::optional MatchMGRSCoords(std::string const & query)
{
- int zoneCode;
- char zoneLetter;
- char squareCode[2];
- std::string eastingStr;
- std::string northingStr;
+ int zone_code;
+ char zone_letter;
+ char square_code[2];
+ string eastingStr;
+ string northingStr;
int32_t easting;
int32_t northing;
strings::SimpleTokenizer it(query, " \t\r");
if (!it)
- return {};
+ return nullopt;
auto token = std::string(*it);
// Parse 2 digit zone code and 1 char zone letter
@@ -126,32 +132,32 @@ std::optional MatchMGRSCoords(std::string const & query)
char dig1 = token[0];
char dig2 = token[1];
if (dig1 < '0' || dig1 > '9' || dig2 < '0' || dig2 > '9')
- return {};
+ return nullopt;
- zoneCode = (dig1 - '0') * 10 + (dig2 - '0');
- if (zoneCode < 1 || zoneCode > 60)
- return {};
+ zone_code = (dig1 - '0') * 10 + (dig2 - '0');
+ if (zone_code < 1 || zone_code > 60)
+ return nullopt;
- zoneLetter = token[2];
+ zone_letter = token[2];
token = token.substr(3);
}
else
- return {};
+ return nullopt;
// Read next token if needed.
if (token.size() == 0)
{
++it;
if (!it)
- return {};
+ return nullopt;
token = std::string(*it);
}
// Parse 2 chars zone code.
if (token.size() >= 2)
{
- squareCode[0] = token[0];
- squareCode[1] = token[1];
+ square_code[0] = token[0];
+ square_code[1] = token[1];
token = token.substr(2);
}
@@ -160,7 +166,7 @@ std::optional MatchMGRSCoords(std::string const & query)
{
++it;
if (!it)
- return {};
+ return nullopt;
token = std::string(*it);
}
@@ -177,7 +183,7 @@ std::optional MatchMGRSCoords(std::string const & query)
{
// eastingStr contains both easting and northing. Let's split
if (eastingStr.size() % 2 != 0)
- return {};
+ return nullopt;
size_t const eastingSize = eastingStr.size() / 2;
northingStr = eastingStr.substr(eastingSize);
@@ -185,10 +191,10 @@ std::optional MatchMGRSCoords(std::string const & query)
}
if (eastingStr.size() != northingStr.size() || eastingStr.size() > 5 || northingStr.size() > 5)
- return {};
+ return nullopt;
if (!strings::to_int32(eastingStr, easting))
- return {};
+ return nullopt;
if (eastingStr.size() < 5)
{
uint64_t const decShift = 5 - eastingStr.size();
@@ -196,14 +202,14 @@ std::optional MatchMGRSCoords(std::string const & query)
}
if (!strings::to_int32(northingStr, northing))
- return {};
+ return nullopt;
if (northingStr.size() < 5)
{
uint64_t const decShift = 5 - northingStr.size();
northing *= base::PowUint(10, decShift);
}
- return utm_mgrs_utils::MGRStoLatLon(easting, northing, zoneCode, zoneLetter, squareCode);
+ return utm_mgrs_utils::MGRStoLatLon(easting, northing, zone_code, zone_letter, square_code);
}
}