feat: Align user report map to Android app

This commit is contained in:
Andrea Busi
2022-11-15 22:40:33 +01:00
parent beb264f95e
commit e9986e0fe1
33 changed files with 340 additions and 128 deletions
@@ -13,7 +13,6 @@
652C37BD26092B3C0068EC3B /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652C37BC26092B3C0068EC3B /* FiltersViewModel.swift */; };
6531185928425B89006CBC29 /* NotificationService+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6531185828425B89006CBC29 /* NotificationService+Extension.swift */; };
65355FFF25F38D3300BB57D2 /* SegnalazioniMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65355FFE25F38D3300BB57D2 /* SegnalazioniMapViewController.swift */; };
6535600425F398CD00BB57D2 /* Costanti+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6535600325F398CD00BB57D2 /* Costanti+Extensions.swift */; };
653604E9262348FA00B2B651 /* EQNBaseMapFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653604E8262348FA00B2B651 /* EQNBaseMapFilter.swift */; };
653C67E225F3CC2E00FE52AC /* EQNCustomAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653C67E125F3CC2E00FE52AC /* EQNCustomAnnotationView.swift */; };
653C67E625F3CC8400FE52AC /* EQNCustomAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 653C67E125F3CC2E00FE52AC /* EQNCustomAnnotationView.swift */; };
@@ -138,6 +137,9 @@
65CB83432915720400EE1E35 /* EQNUserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65CB83422915720400EE1E35 /* EQNUserData.swift */; };
65D409942619BA34008CF356 /* SegnalazioniSendReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D409932619BA34008CF356 /* SegnalazioniSendReportCell.swift */; };
65D9938A29219DEC00F2B0EB /* UIKit+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */; };
65D9938C2922647800F2B0EB /* AppTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC52B8A424FCCD6900ABEBA6 /* AppTheme.swift */; };
65D9938E292264F800F2B0EB /* Foundation+EQNExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D9938D292264F800F2B0EB /* Foundation+EQNExtensions.swift */; };
65D9938F2922652100F2B0EB /* Foundation+EQNExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D9938D292264F800F2B0EB /* Foundation+EQNExtensions.swift */; };
65DBFB4B25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DBFB4A25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift */; };
65DBFB7425E2BBF20041CBA6 /* GADTMediumTemplateView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 65DBFB6F25E2BBF20041CBA6 /* GADTMediumTemplateView.xib */; };
65DBFB7525E2BBF20041CBA6 /* GADTMediumTemplateView.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DBFB7125E2BBF20041CBA6 /* GADTMediumTemplateView.m */; };
@@ -296,7 +298,6 @@
652C37BC26092B3C0068EC3B /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = "<group>"; };
6531185828425B89006CBC29 /* NotificationService+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationService+Extension.swift"; sourceTree = "<group>"; };
65355FFE25F38D3300BB57D2 /* SegnalazioniMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniMapViewController.swift; sourceTree = "<group>"; };
6535600325F398CD00BB57D2 /* Costanti+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Costanti+Extensions.swift"; sourceTree = "<group>"; };
653604E8262348FA00B2B651 /* EQNBaseMapFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBaseMapFilter.swift; sourceTree = "<group>"; };
653C67E125F3CC2E00FE52AC /* EQNCustomAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNCustomAnnotationView.swift; sourceTree = "<group>"; };
653C67FB25F3D63500FE52AC /* EQNMapAnnotationUserReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNMapAnnotationUserReport.swift; sourceTree = "<group>"; };
@@ -428,6 +429,7 @@
65BBB22B26064BE6005D6CDF /* SegnalazioniLast24HoursCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniLast24HoursCell.swift; sourceTree = "<group>"; };
65CB83422915720400EE1E35 /* EQNUserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNUserData.swift; sourceTree = "<group>"; };
65D409932619BA34008CF356 /* SegnalazioniSendReportCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegnalazioniSendReportCell.swift; sourceTree = "<group>"; };
65D9938D292264F800F2B0EB /* Foundation+EQNExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+EQNExtensions.swift"; sourceTree = "<group>"; };
65DBFB4A25E29DD60041CBA6 /* SeismicNetworksMapDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeismicNetworksMapDetailViewController.swift; sourceTree = "<group>"; };
65DBFB6F25E2BBF20041CBA6 /* GADTMediumTemplateView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = GADTMediumTemplateView.xib; sourceTree = "<group>"; };
65DBFB7025E2BBF20041CBA6 /* GADTTemplateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GADTTemplateView.h; sourceTree = "<group>"; };
@@ -714,7 +716,6 @@
isa = PBXGroup;
children = (
8CF66050214C0F7F009F4314 /* Costanti.h */,
6535600325F398CD00BB57D2 /* Costanti+Extensions.swift */,
8CBD3DC52149B9AD0070C963 /* AppDelegate.h */,
8CBD3DC62149B9AD0070C963 /* AppDelegate.m */,
DCB6FBEA24D0B11300ED23B8 /* Controllers */,
@@ -772,6 +773,7 @@
8C7A3B65225A5EA40045B266 /* NSDictionary+EQNExtensions.h */,
8C7A3B64225A5EA30045B266 /* NSDictionary+EQNExtensions.m */,
65E1B19A260F980600A0ACBA /* Dictionary+EQNExtensions.swift */,
65D9938D292264F800F2B0EB /* Foundation+EQNExtensions.swift */,
65583A04261B83BE00ECA9F9 /* UIKit+Extensions.swift */,
650B23AA2632CCD3007AE752 /* UIView+EQNExtensions.swift */,
);
@@ -1639,6 +1641,7 @@
8C4E34422152B5E8008B0D2A /* EQNRilevamento.m in Sources */,
8C7A3B66225A5EA40045B266 /* NSDictionary+EQNExtensions.m in Sources */,
8CF66053214C12DC009F4314 /* EQNMath.m in Sources */,
65D9938E292264F800F2B0EB /* Foundation+EQNExtensions.swift in Sources */,
DCF4A54524F8DB8300B17326 /* SettingDateTableViewCell.swift in Sources */,
DC7EEE4A252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift in Sources */,
DC7EEE4F252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift in Sources */,
@@ -1653,7 +1656,6 @@
8CBD3DC72149B9AD0070C963 /* AppDelegate.m in Sources */,
DC974AFF251748B300A139EC /* SeismicFiltersViewController.swift in Sources */,
DC105641251E7ECE002579BB /* UIFont+Extensions.swift in Sources */,
6535600425F398CD00BB57D2 /* Costanti+Extensions.swift in Sources */,
DCB28CEE24FB8400001F557E /* SettingsViewController.swift in Sources */,
DCB528212560161C005288E5 /* AlertSimulatorViewController.swift in Sources */,
DCC76BD8251F56050005C4DC /* SeismicCardSettingsViewController.swift in Sources */,
@@ -1674,11 +1676,13 @@
buildActionMask = 2147483647;
files = (
8C465D9F21F7BE0600F04673 /* Assets.xcassets in Sources */,
65D9938C2922647800F2B0EB /* AppTheme.swift in Sources */,
DC0AE1B92538204100111307 /* EQNSegnalazione.m in Sources */,
654D18DE25F943E200BB6DB0 /* EQNMapAnnotationUserReport.swift in Sources */,
654D18D625F9420500BB6DB0 /* EQNMapAnnotationPastquake.swift in Sources */,
DC0AE1B52538202300111307 /* EQNUtility.m in Sources */,
653C67E625F3CC8400FE52AC /* EQNCustomAnnotationView.swift in Sources */,
65D9938F2922652100F2B0EB /* Foundation+EQNExtensions.swift in Sources */,
654D18DA25F9424700BB6DB0 /* EQNUtility+Extensions.swift in Sources */,
DC0AE1BA2538204100111307 /* EQNPastquakes.m in Sources */,
65D9938A29219DEC00F2B0EB /* UIKit+Extensions.swift in Sources */,
@@ -65,6 +65,8 @@ class PasquakesMapViewController: EQNBaseMapViewController {
}
override func didTapAnnotation(_ annotation: MKAnnotation) {
mapView.deselectAnnotation(annotation, animated: true)
guard let annotation = annotation as? EQNMapAnnotationPastquake, let pastquake = annotation.pastquake else {
return
}
@@ -16,10 +16,74 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
let circle: MKCircle
}
override var isCloseButtonVisible: Bool {
false
}
/// Contains circles and related colors to draw overlays on the map
private var mapCircles = [MapCircle]()
/// Reports currently showned on the map
private var filteredReports = [EQNSegnalazione]()
/// Defines if time has to be shown on map annotations
private var showAnnotationTime = false
// MARK: - UI
// app icon and name displayed on the screenshot
private lazy var watermarkView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.isHidden = true
let logo = UIImageView(image: .init(named: "eq_icon_transparent"))
logo.translatesAutoresizingMaskIntoConstraints = false
logo.contentMode = .scaleAspectFit
view.addSubview(logo)
logo.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
logo.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
logo.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
logo.widthAnchor.constraint(equalTo: logo.heightAnchor).isActive = true
let title = UILabel()
title.translatesAutoresizingMaskIntoConstraints = false
title.text = NSLocalizedString("app_name", comment: "") + " App"
title.textColor = AppTheme.Colors.red
title.font = .preferredFont(for: .title3, weight: .semibold)
view.addSubview(title)
title.leadingAnchor.constraint(equalTo: logo.trailingAnchor, constant: 10.0).isActive = true
title.centerYAnchor.constraint(equalTo: logo.centerYAnchor).isActive = true
title.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
return view
}()
private lazy var magnitudeLegendView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fillEqually
[20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120].forEach { magnitude in
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = (magnitude / 10).toRoman()
label.backgroundColor = UIColor(named: "Mercalli \(magnitude)")
label.textAlignment = .center
label.font = .preferredFont(forTextStyle: .callout)
label.textColor = magnitude >= 100 ? .white : .black
stackView.addArrangedSubview(label)
}
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
return view
}()
// MARK: - View Lifecycle
@@ -29,8 +93,31 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
// MARK: - Public
override func extraUI() {
view.addSubview(magnitudeLegendView)
view.addSubview(watermarkView)
magnitudeLegendView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
magnitudeLegendView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
magnitudeLegendView.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
magnitudeLegendView.topAnchor.constraint(equalTo: mapView.topAnchor).isActive = true
watermarkView.topAnchor.constraint(equalTo: mapView.topAnchor, constant: 10.0).isActive = true
watermarkView.leadingAnchor.constraint(equalTo: mapView.leadingAnchor, constant: 10.0).isActive = true
watermarkView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
}
override func configureUI() {
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(onTapCloseButton(_:)))
navigationItem.rightBarButtonItems = [
UIBarButtonItem(image: UIImage(named: "navbar-icon-screenshot"), style: .plain, target: self, action: #selector(onTapScreenshotButton(_:))),
UIBarButtonItem(image: UIImage(named: "navbar-icon-pin-arrow"), style: .plain, target: self, action: #selector(onTapMapDetailStyleButton(_:)))
]
}
override func registerMapAnnotationViews() {
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SingleLineIdentifier)
mapView.register(EQNCustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: EQNCustomAnnotationView.SmallIdentifier)
}
override func loadDataSource() {
@@ -96,27 +183,41 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
}
}
override func didTapAnnotation(_ annotation: MKAnnotation) {
guard let annotation = annotation as? EQNMapAnnotationUserReport, let report = annotation.report else {
return
// MARK: - Actions
@objc private func onTapCloseButton(_ sender: Any) {
dismiss(animated: true)
}
@objc private func onTapMapDetailStyleButton(_ sender: Any) {
showAnnotationTime.toggle()
loadDataSource()
}
@objc private func onTapScreenshotButton(_ sender: Any) {
let snapshot = createSnapshot()
let controller = UIActivityViewController(activityItems: [snapshot], applicationActivities: [])
present(controller, animated: true)
}
public func createSnapshot() -> UIImage {
// mostriamo il watermark e nascondiamo la legenda
watermarkView.isHidden = false
magnitudeLegendView.isHidden = true
// riduciamo la porzione da salvare alla sola mappa (eliminiamo i filtri)
let size = CGSize(width: view.bounds.width, height: mapView.bounds.maxY)
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { ctx in
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
}
let difference = Int(Date().timeIntervalSince(report.date) / 60.0)
let title = EQNUtility.formattedString(forTimeDifference: difference) + " - \(report.intensity.description)"
// torniamo allo stato originale
watermarkView.isHidden = true
magnitudeLegendView.isHidden = false
var message = ""
+ "🏢 " + report.address
+ "\n" + EQNUtility.formattedDate(from: report.date) + " \(NSLocalizedString("share_yourtime", comment: ""))"
if !report.message.isEmpty {
message += "\n💬 \(report.message)"
}
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("main_share", comment: ""), style: .default, handler: { [unowned self] _ in
self.openShareActivity(for: report)
}))
alert.addAction(UIAlertAction(title: NSLocalizedString("official_close", comment: ""), style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
return image
}
// MARK: - Private
@@ -125,7 +226,7 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
let vector_latitude = reports.map { $0.coordinate.coordinate.latitude }
let vector_longitude = reports.map { $0.coordinate.coordinate.longitude }
let vector_date = reports.map { $0.date }
let vector_state = reports.map { $0.intensity.rawValue }
let vector_state = reports.map { $0.intensity }
let minutes: TimeInterval = filter.minutes
@@ -200,21 +301,7 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
}
let circles = Array(0..<cluster_code).map { (i) -> MapCircle in
var value_distance = max_distance[i] / 20.0
if value_distance > 1.0 {
value_distance = 1.0
}
let value_intensity = (cluster_intensity[i]-1.0) / 2.0
let value_reference = max(value_distance, value_intensity)
let color: UIColor
if value_reference <= 0.5 {
let red = round(value_reference * 510)
color = UIColor(red: CGFloat(red / 255.0), green: 230.0/255.0, blue: 0.0, alpha: 1.0)
} else {
let green = round(230 - (value_reference - 0.5) * 460)
color = UIColor(red: 255.0, green: CGFloat(green / 255.0), blue: 0.0, alpha: 1.0)
}
let color: UIColor = AppTheme.Colors.darkGray
let centre = CLLocation(latitude: lat_centre[i], longitude: lon_centre[i])
let farest = CLLocation(latitude: lat_farest[i], longitude: lon_farest[i])
@@ -244,25 +331,6 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
Date().timeIntervalSince(date) / 60.0
}
private func openShareActivity(for report: EQNSegnalazione) {
// create message to share
let intensity = report.intensity.description
let difference = Int(Date().timeIntervalSince(report.date) / 60.0)
let time = EQNUtility.formattedString(forTimeDifference: difference)
let message = [
NSLocalizedString("share_hashtag", comment: ""),
intensity,
NSLocalizedString("share_felt", comment: ""),
report.address,
time + ".",
NSLocalizedString("share_notified", comment: "")
].joined(separator: " ")
let controller = UIActivityViewController(activityItems: [message], applicationActivities: [])
present(controller, animated: true)
}
// MARK: - Map
override func setupAnnotationView(for annotation: MKAnnotation, on mapView: MKMapView) -> MKAnnotationView? {
@@ -270,10 +338,12 @@ class SegnalazioniMapViewController: EQNBaseMapViewController {
return nil
}
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: EQNCustomAnnotationView.SingleLineIdentifier, for: annotation) as! EQNCustomAnnotationView
let identifier = showAnnotationTime ? EQNCustomAnnotationView.SingleLineIdentifier : EQNCustomAnnotationView.SmallIdentifier
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: annotation) as! EQNCustomAnnotationView
annotationView.image = annotation.image
annotationView.title = annotation.title
annotationView.title = annotation.timeDifference
annotationView.canShowCallout = true
return annotationView
}
@@ -91,7 +91,8 @@
- (IBAction)openMapTapped:(id)sender
{
SegnalazioniMapViewController *controller = [[SegnalazioniMapViewController alloc] init];
[self presentViewController:controller animated:YES completion:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navController animated:YES completion:nil];
}
- (IBAction)openTwitterTapped:(id)sender
@@ -107,6 +107,8 @@ class SeismicNetworksMapDetailViewController: EQNBaseMapViewController {
}
override func didTapAnnotation(_ annotation: MKAnnotation) {
mapView.deselectAnnotation(annotation, animated: true)
guard let annotation = annotation as? EQNMapAnnotationSeismic else { return }
let viewModel = SeismicNetworkViewModel(seismic: annotation.seismic)
@@ -27,6 +27,11 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
!availableFilters.isEmpty
}
/// If `true` the close button will be shown on top of the map view
var isCloseButtonVisible: Bool {
true
}
// MARK: - Internal
/// Annotations displayed on the map
@@ -116,13 +121,14 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
private func setupUI() {
view.backgroundColor = .white
view.addSubview(mapView)
view.addSubview(closeButton)
view.addSubview(containerView)
if isFilterViewVisible {
view.addSubview(filtersView)
}
closeButton.addDefaultConstraint(to: view)
if isCloseButtonVisible {
view.addSubview(closeButton)
closeButton.addDefaultConstraint(to: view)
}
containerView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
@@ -145,6 +151,8 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: (isFilterViewVisible ? filtersView : containerView).topAnchor).isActive = true
extraUI()
}
// MARK: - View Lifecycle
@@ -152,6 +160,7 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
registerMapAnnotationViews()
loadDataSource()
}
@@ -167,6 +176,16 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
// MARK: - Public
/// Add extra UI not available in the base class
func extraUI() {
// nope, subclass will implement some logic
}
/// Configure UI after view initialization
func configureUI() {
// nope, subclass will implement some logic
}
/// Load data to display on the map
func loadDataSource() {
// nope, subclass will implement some logic
@@ -277,7 +296,5 @@ class EQNBaseMapViewController: EQNBaseViewController, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let annotation = view.annotation else { return }
didTapAnnotation(annotation)
mapView.deselectAnnotation(annotation, animated: true)
}
}
@@ -1,21 +0,0 @@
//
// Costanti+Extensions.swift
// Earthquake Network
//
// Created by Andrea Busi on 06/03/21.
// Copyright © 2021 Earthquake Network. All rights reserved.
//
import Foundation
extension EQNPastquakeIntensity {
var description: String {
switch self {
case .mild: return NSLocalizedString("widget_mild", comment: "")
case .strong: return NSLocalizedString("widget_strong", comment: "")
case .veryStrong: return NSLocalizedString("widget_verystring", comment: "")
}
}
}
+1 -1
View File
@@ -55,7 +55,7 @@ static NSString * const EQNServerUrlDownloadAreaCheck = @"https://srv.earthquake
// download pastquakes
static NSString * const EQNServerUrlDownloadPastQuakes = @"https://srv.earthquakenetwork.it/distquake_download_pastquakes.php";
// download segnalazioni
static NSString * const EQNServerUrlDownloadUserReports = @"https://srv.earthquakenetwork.it/distquake_download_manual.php";
static NSString * const EQNServerUrlDownloadUserReports = @"https://srv.earthquakenetwork.it/distquake_download_manual3.php";
// Invio segnalazione
static NSString * const EQNServerUrlSendUserReport = @"https://srv.earthquakenetwork.it/distquake_upload_manual4.php";
static NSString * const EQNServerUrlSendUserReportMessage = @"https://srv.earthquakenetwork.it/distquake_upload_manual_message.php";
@@ -0,0 +1,29 @@
//
// Foundation+EQNExtensions.swift
// Earthquake Network
//
// Created by Andrea Busi on 14/11/22.
// Copyright © 2022 Earthquake Network. All rights reserved.
//
import Foundation
extension Int {
func toRoman() -> String {
let conversionTable: [(intNumber: Int, romanNumber: String)] =
[(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"),
(90, "XC"), (50, "L"), (40, "XL"), (10, "X"),
(9, "IX"), (5, "V"), (4, "IV"), (1, "I")]
var roman = ""
var remainder = 0
for entry in conversionTable {
let quotient = (self - remainder) / entry.intNumber
remainder += quotient * entry.intNumber
roman += String(repeating: entry.romanNumber, count: quotient)
}
return roman
}
}
@@ -24,6 +24,32 @@ extension UIButton {
}
extension UIImage {
class func circle(
diameter: CGFloat,
color: UIColor,
borderWidth: CGFloat = 0.0,
borderColor: UIColor = .black
) -> UIImage {
let size = CGSize(width: diameter, height: diameter)
let renderer = UIGraphicsImageRenderer(size: size)
let img = renderer.image { ctx in
ctx.cgContext.setFillColor(color.cgColor)
ctx.cgContext.setStrokeColor(borderColor.cgColor)
ctx.cgContext.setLineWidth(borderWidth)
// reduce circle size to keep space for the border
// without this, the image view is cropped
let circleDiameter = diameter - 2*borderWidth
let rectangle = CGRect(x: borderWidth, y: borderWidth, width: circleDiameter, height: circleDiameter)
ctx.cgContext.addEllipse(in: rectangle)
ctx.cgContext.drawPath(using: .fillStroke)
}
return img
}
}
extension UIView {
/// Creates a snapshot of the current view
/// - Returns: Image with the snapshot of the view
@@ -9,23 +9,15 @@
@import Foundation;
@import CoreLocation;
// Intensità terremoti segnalazioni utente
typedef NS_CLOSED_ENUM(NSInteger, EQNPastquakeIntensity) {
EQNPastquakeIntensityMild = 1,
EQNPastquakeIntensityStrong = 2,
EQNPastquakeIntensityVeryStrong = 3
};
NS_ASSUME_NONNULL_BEGIN
@interface EQNSegnalazione : NSObject
@property (nonatomic, strong) CLLocation *coordinate;
@property (nonatomic, strong) NSString *address;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic) NSInteger difference;
@property (nonatomic) EQNPastquakeIntensity intensity;
@property (nonatomic, strong) NSString *message;
// values are from 20 to 120
@property (nonatomic) NSInteger intensity;
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
@@ -17,15 +17,12 @@
{
self = [super init];
if (self) {
double latitude = [dictionary[@"latitude"] doubleValue];
double longitude = [dictionary[@"longitude"] doubleValue];
double latitude = [dictionary[@"la"] doubleValue];
double longitude = [dictionary[@"lo"] doubleValue];
_coordinate = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
_address = dictionary[@"address"];
NSDate *date = [EQNUtility getDateFromString:dictionary[@"date"]];
NSDate *date = [EQNUtility getDateFromString:dictionary[@"dt"]];
_date = date == nil ? [NSDate date] : date;
_difference = [dictionary[@"difference"] integerValue];
_intensity = [dictionary[@"magnitude"] integerValue];
_message = dictionary[@"msg"];
_intensity = [dictionary[@"ma"] integerValue];
}
return self;
}
@@ -34,24 +31,18 @@
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.address forKey:@"address"];
[encoder encodeObject:self.date forKey:@"date"];
[encoder encodeInteger:self.difference forKey:@"difference"];
[encoder encodeObject:self.coordinate forKey:@"coordinate"];
[encoder encodeInteger:self.intensity forKey:@"intensity"];
[encoder encodeObject:self.message forKey:@"message"];
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self) {
self.address = [decoder decodeObjectForKey:@"address"];
self.date = [decoder decodeObjectForKey:@"date"];
self.difference = [decoder decodeIntegerForKey:@"difference"];
self.coordinate = [decoder decodeObjectForKey:@"coordinate"];
self.intensity = [decoder decodeIntegerForKey:@"intensity"];
self.message = [decoder decodeObjectForKey:@"message"];
}
return self;
}
@@ -15,36 +15,60 @@ class EQNMapAnnotationUserReport: NSObject, MKAnnotation {
@objc var coordinate: CLLocationCoordinate2D
@objc var title: String?
@objc var subtitle: String?
@objc var image: UIImage?
@objc var timeDifference: String?
var report: EQNSegnalazione?
// MARK: - Init
convenience init(report: EQNSegnalazione) {
let coordinate = CLLocationCoordinate2D(latitude: report.coordinate.coordinate.latitude, longitude: report.coordinate.coordinate.longitude)
let magnitude = report.intensity.rawValue
let title = EQNUtility.formattedTimeDifference(from: report.date)
let coordinate = report.coordinate.coordinate
let magnitude = report.intensity
self.init(title: title, coordinate: coordinate, magnitude: magnitude)
self.init(magnitude: magnitude, coordinate: coordinate)
self.report = report
self.timeDifference = EQNUtility.formattedTimeDifference(from: report.date)
}
@objc init(title: String, coordinate: CLLocationCoordinate2D, magnitude: Int) {
self.title = title
self.image = Self.image(for: magnitude)
@objc init(
magnitude: Int,
coordinate: CLLocationCoordinate2D
) {
self.title = Self.title(for: magnitude)
self.subtitle = Self.subtitle(for: magnitude)
self.coordinate = coordinate
super.init()
self.image = Self.image(for: magnitude)
}
// MARK: - Public
// MARK: - Helper
private static func image(for magnitute: Int) -> UIImage? {
switch magnitute {
case 1: return UIImage(named: "star_report_green")
case 2: return UIImage(named: "star_report_yellow")
case 3: return UIImage(named: "star_report_red")
default: return nil
guard let color = UIColor(named: "Mercalli \(magnitute)") else {
print("[EQNMapAnnotationUserReport] Unable to get a color for magnitude: \(magnitute)")
return nil
}
return UIImage.circle(diameter: 24.0, color: color, borderWidth: 1.0, borderColor: AppTheme.Colors.gray)
}
private static func title(for magnitude: Int) -> String {
let grade = magnitude / 10
let roman = grade.toRoman()
return String(format: NSLocalizedString("mercalli_intensity", comment: ""), roman)
}
private static func subtitle(for magnitude: Int) -> String {
let grade = magnitude / 10
let roman = grade.toRoman()
let string = NSLocalizedString("mercalli_\(roman)", comment: "")
// strings are in the format like "II - Appena percepito"
// so we are going to remove the "II" from the string
let components = string.components(separatedBy: " - ")
if components.count > 1 {
return components[1]
}
return string
}
}
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "eq_icon_transparent.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "navbar-icon-pin-arrow.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "navbar-icon-pin-arrow@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "navbar-icon-pin-arrow@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "navbar-icon-screenshot.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "navbar-icon-screenshot@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "navbar-icon-screenshot@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
// Copyright © 2020 Earthquake Network. All rights reserved.
//
import Foundation
import UIKit
@objcMembers
@@ -13,6 +13,7 @@ import MapKit
@objc
public class EQNCustomAnnotationView: MKAnnotationView {
@objc static let SmallIdentifier = "EQNCustomAnnotationViewSmall"
@objc static let SingleLineIdentifier = "EQNCustomAnnotationViewSingleLine"
@objc static let DoubleLineIdentifier = "EQNCustomAnnotationViewDoubleLine"
@@ -67,9 +68,12 @@ public class EQNCustomAnnotationView: MKAnnotationView {
backgroundColor = .clear
if reuseIdentifier == Self.SingleLineIdentifier {
if reuseIdentifier == Self.SmallIdentifier {
frame = CGRect(x: 0, y: 0, width: 16, height: 16)
setupSmallUI()
} else if reuseIdentifier == Self.SingleLineIdentifier {
frame = CGRect(x: 0, y: 0, width: 40, height: 40)
setupUI()
setupSingleLineUI()
} else if reuseIdentifier == Self.DoubleLineIdentifier {
frame = CGRect(x: 0, y: 0, width: 40, height: 55)
setupDoubleLineUI()
@@ -82,7 +86,12 @@ public class EQNCustomAnnotationView: MKAnnotationView {
// MARK: - Private
private func setupUI() {
private func setupSmallUI() {
imageView.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(imageView)
}
private func setupSingleLineUI() {
let labelFrame = CGRect(x: 0, y: 0, width: frame.width, height: Self.HeightLabel)
labelTop.frame = labelFrame
addSubview(labelTop)
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Ολόκληρα κτίρια κατέρρευσαν";
"mercalli_XI" = "XI - Καταστροφές ολόκληρων αστικών κέντρων, πολλά θύματα, ρωγμές στο έδαφος και κατολισθήσεις";
"mercalli_XII" = "XII - Διάσπαση του εδάφους, μετατόπιση του φλοιού της γης";
"mercalli_intensity" = "Ένταση %@";
// values
"official_magnitude_value_00" = "Μέγεθος >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Whole buildings collapsed";
"mercalli_XI" = "XI - Destruction of entire urban centers, many victims, crevices in the ground and landslides";
"mercalli_XII" = "XII - Disruption of the soil, displacement of the earth's crust";
"mercalli_intensity" = "Intensity %@";
// values
"official_magnitude_value_00" = "Magnitude >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Edificios enteros se derrumbaron";
"mercalli_XI" = "XI - Destrucción de centros urbanos enteros, muchas víctimas, grietas en el suelo y deslizamientos de tierra";
"mercalli_XII" = "XII - Alteración del suelo, desplazamiento de la corteza terrestre.";
"mercalli_intensity" = "Intensidad %@";
// values
"official_magnitude_value_00" = "Magnitud >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Des bâtiments entiers se sont effondrés";
"mercalli_XI" = "XI - Destruction de centres urbains entiers, nombreuses victimes, crevasses dans le sol et glissements de terrain";
"mercalli_XII" = "XII - Perturbation du sol, déplacement de la croûte terrestre";
"mercalli_intensity" = "Intensité %@";
// values
"official_magnitude_value_00" = "Magnitude >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Čitave zgrade srušene";
"mercalli_XI" = "XI - Uništavanje čitavih urbanih središta, mnogo žrtava, pukotine u zemlji i klizišta";
"mercalli_XII" = "XII - Poremećaj tla, pomicanje zemljine kore";
"mercalli_intensity" = "Intenzitet %@";
// values
"official_magnitude_value_00" = "Jačina >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Seluruh bangunan runtuh";
"mercalli_XI" = "XI - Penghancuran seluruh pusat kota, banyak korban, celah-celah di tanah dan tanah longsor";
"mercalli_XII" = "XII - Gangguan tanah, perpindahan kerak bumi";
"mercalli_intensity" = "Intensitas %@";
// values
"official_magnitude_value_00" = "Magnitudo >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Interi edifici crollati";
"mercalli_XI" = "XI - Distruzione di interi centri urbani, moltissime vittime, crepacci nel suolo e frane ";
"mercalli_XII" = "XII - Sconvolgimento del suolo, dislocamento della crosta terrestre";
"mercalli_intensity" = "Intensità %@";
// values
"official_magnitude_value_00" = "Magnitudo >= 0.0";
@@ -159,6 +159,7 @@
"mercalli_X" = "X - Bütün binalar çöktü";
"mercalli_XI" = "XI - Tüm şehir merkezlerinin, birçok kurbanın, zemindeki yarıkların ve toprak kaymalarının yok edilmesi";
"mercalli_XII" = "XII - Toprağın bozulması, yer kabuğunun yer değiştirmesi";
"mercalli_intensity" = "Şiddeti %@";
// values
"official_magnitude_value_00" = "Büyüklük >= 0.0";