Last active
April 25, 2020 03:57
-
-
Save kgao/4d71ddad3554ff8024e8518e5a6fd293 to your computer and use it in GitHub Desktop.
mac dev tips
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
code () { VSCODE_CWD="$PWD" open -n -b "com.microsoft.VSCode" --args $* ;} | |
///: A MapKit based Playground | |
import PlaygroundSupport | |
import UIKit | |
class ReverseGeocodingViewController: UIViewController { | |
// MARK: - Properties | |
@IBOutlet var latitudeTextField: UITextField! | |
@IBOutlet var longitudeTextField: UITextField! | |
@IBOutlet var geocodeButton: UIButton! | |
@IBOutlet var activityIndicatorView: UIActivityIndicatorView! | |
@IBOutlet var locationLabel: UILabel! | |
// MARK: - View Life Cycle | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
} | |
lazy var geocoder = CLGeocoder() | |
@IBAction func geocode(_ sender: UIButton) { | |
guard let latAsString = latitudeTextField.text, let lat = Double(latAsString) else { return } | |
guard let lngAsString = longitudeTextField.text, let lng = Double(lngAsString) else { return } | |
// Create Location | |
let location = CLLocation(latitude: lat, longitude: lng) | |
// Geocode Location | |
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in | |
// Process Response | |
self.processResponse(withPlacemarks: placemarks, error: error) | |
} | |
// Update View | |
geocodeButton.isHidden = true | |
activityIndicatorView.startAnimating() | |
} | |
private func processResponse(withPlacemarks placemarks: [CLPlacemark]?, error: Error?) { | |
// Update View | |
geocodeButton.isHidden = false | |
activityIndicatorView.stopAnimating() | |
if let error = error { | |
print("Unable to Reverse Geocode Location (\(error))") | |
locationLabel.text = "Unable to Find Address for Location" | |
} else { | |
if let placemarks = placemarks, let placemark = placemarks.first { | |
locationLabel.text = placemark.compactAddress | |
} else { | |
locationLabel.text = "No Matching Addresses Found" | |
} | |
} | |
} | |
} | |
extension CLPlacemark { | |
var compactAddress: String? { | |
if let name = name { | |
var result = name | |
if let street = thoroughfare { | |
result += ", \(street)" | |
} | |
if let city = locality { | |
result += ", \(city)" | |
} | |
if let country = country { | |
result += ", \(country)" | |
} | |
return result | |
} | |
return nil | |
} | |
} | |
// Forward Geocoding With Clgeocoder | |
let geocoder = CLGeocoder() | |
let address = "8787 Snouffer School Rd, Montgomery Village, MD 20879" | |
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in | |
if((error) != nil){ | |
print("Error", error ?? "") | |
} | |
if let placemark = placemarks?.first { | |
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate | |
//print("Lat: \(coordinates.latitude) | Long: \(coordinates.longitude)") | |
} | |
}) | |
// Specify the place data types to return place - likelyhood. | |
//let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) | | |
// UInt(GMSPlaceField.placeID.rawValue))! | |
//placesClient?.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: fields, callback: { | |
// (placeLikelihoodList: Array<GMSPlaceLikelihood>?, error: Error?) in | |
// if let error = error { | |
// print("An error occurred: \(error.localizedDescription)") | |
// return | |
// } | |
// | |
// if let placeLikelihoodList = placeLikelihoodList { | |
// for likelihood in placeLikelihoodList { | |
// let place = likelihood.place | |
// print("Current Place name \(String(describing: place.name)) at likelihood \(likelihood.likelihood)") | |
// print("Current PlaceID \(String(describing: place.placeID))") | |
// } | |
// } | |
//}) | |
//async,wait | |
class AsyncOperation { | |
private let semaphore: DispatchSemaphore | |
private let dispatchQueue: DispatchQueue | |
typealias CompleteClosure = ()->() | |
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) { | |
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions) | |
dispatchQueue = DispatchQueue(label: dispatchQueueLabel) | |
} | |
func run(closure: @escaping (@escaping CompleteClosure)->()) { | |
dispatchQueue.async { | |
self.semaphore.wait() | |
closure { | |
self.semaphore.signal() | |
} | |
} | |
} | |
} | |
import MapKit | |
//-------------------------------------- | |
// Draw on the map - local test: | |
let appleParkWayCoordinates = CLLocationCoordinate2DMake(40.7569, -73.9828) | |
// Now let's create a MKMapView | |
let mapView = MKMapView(frame: CGRect(x:0, y:0, width:800, height:800)) | |
// Define a region for our map view | |
var mapRegion = MKCoordinateRegion() | |
let mapRegionSpan = 0.02 | |
mapRegion.center = appleParkWayCoordinates | |
mapRegion.span.latitudeDelta = mapRegionSpan | |
mapRegion.span.longitudeDelta = mapRegionSpan | |
mapView.setRegion(mapRegion, animated: true) | |
// Create a map annotation | |
let annotation = MKPointAnnotation() | |
annotation.coordinate = appleParkWayCoordinates | |
annotation.title = "Apple Inc." | |
annotation.subtitle = "One Apple Park Way, Cupertino, California." | |
mapView.addAnnotation(annotation) | |
// Add the created mapView to our Playground Live View | |
PlaygroundPage.current.liveView = mapView | |
/// -------------------------------------------------------------------------- | |
/// | |
/// | |
/// CovidSafe | |
/// | |
/// | |
/// ---------------------------------------- Coordinate <> Locationm -------------------------------------------- | |
import CoreLocation | |
// draw on the map | |
func drawMap(lat:Double,lon:Double,address:CLPlacemark){ | |
//--------------------- Draw on the map ----------------- | |
let appleParkWayCoordinates = CLLocationCoordinate2DMake(lat, lon) | |
// Now let's create a MKMapView | |
let mapView = MKMapView(frame: CGRect(x:0, y:0, width:800, height:800)) | |
// Define a region for our map view | |
var mapRegion = MKCoordinateRegion() | |
let mapRegionSpan = 0.02 | |
mapRegion.center = appleParkWayCoordinates | |
mapRegion.span.latitudeDelta = mapRegionSpan | |
mapRegion.span.longitudeDelta = mapRegionSpan | |
mapView.setRegion(mapRegion, animated: true) | |
// Create a map annotation | |
let annotation = MKPointAnnotation() | |
annotation.coordinate = appleParkWayCoordinates | |
annotation.title = address.name! //Buiness name? Google API place likelyhood. | |
annotation.subtitle = address.compactAddress! | |
mapView.addAnnotation(annotation) | |
// Add the created mapView to our Playground Live View | |
PlaygroundPage.current.liveView = mapView | |
//-------------------------------------- | |
} | |
//Test input: | |
let lat = 40.75 | |
let lon = -73.98 | |
var placeList: [CLPlacemark] = [] //List of CLPlacemarks | |
//Batch input: | |
struct Geo : Codable { | |
let lat : Double | |
let lon : Double | |
let timestamp: Double | |
} | |
struct AddressTS : Codable { | |
let address : String | |
let timestamp: Double | |
} | |
struct AddressPeriod : Codable { | |
let address: String | |
let period: String | |
} | |
//input type 1 | |
let geoList = [Geo(lat:40.75,lon:-73.98,timestamp:1587749400), | |
Geo(lat:40.75,lon:-73.98,timestamp:1587751200), | |
Geo(lat:40.76,lon:-73.99,timestamp:1587752000), | |
Geo(lat:40.76,lon:-73.99,timestamp:1587762000), | |
Geo(lat:40.77,lon:-74.00,timestamp:1587770000)] | |
do { | |
//encode: struct to json | |
let jsonData = try JSONEncoder().encode(geoList) | |
let jsonString = String(data: jsonData, encoding: .utf8)! | |
print(jsonString) // [{"lat":40.75,"lon":-73.98,"timestamp":1587749400},{"lat":40.75,"lon":-73.98,"timestamp":1587751200}] | |
//decode: json to struct | |
let decodedGeos = try JSONDecoder().decode([Geo].self, from: jsonData) | |
print(decodedGeos) //[__lldb_expr_166.Geo(lat: 40.75, lon: -73.98, timestamp: 1587749400), __lldb_expr_166.Geo(lat: 40.75, lon: -73.98, timestamp: 1587751200)] | |
//decode json string to struct | |
let JSON = """ | |
[{ "lat":40.75, | |
"lon":-73.98, | |
"timestamp":1587749400 | |
}, | |
{ | |
"lat":40.75, | |
"lon":-73.98, | |
"timestamp":1587751200 | |
}] | |
""" | |
//input type 2 | |
let test:[Geo] = try! JSONDecoder().decode([Geo].self, from: JSON.data(using: .utf8)!) | |
print(test[0].timestamp) | |
} catch { print(error) } | |
// Step 1: convert JSON to geoList input, then use the method - batchReserseGeocodeLocation: | |
func batchReserseGeocodeLocation(geoList:[Geo], completionHandler:@escaping([AddressTS]) -> ()){ //Please specify your type as contract to use | |
// Fire an asynchronous callback when all your requests finish in synchronous. | |
let asyncGroup = DispatchGroup() | |
var placeList: [AddressTS] = [AddressTS](repeating: AddressTS(address:"",timestamp:0), count: geoList.count) //List of AddressTS | |
for (index, geo) in geoList.enumerated() | |
{ asyncGroup.enter() | |
//Reverse Geocoding With Clgeocoder | |
let gecoder = CLGeocoder.init() | |
gecoder.reverseGeocodeLocation(CLLocation.init(latitude:geo.lat,longitude: geo.lon)) { (addresses, error) in | |
if error == nil{ | |
if let address = addresses{ | |
//Async: Here you can get all the info by combining that you can make address! | |
print("Address \(index) : \(address)") | |
//async - availability first | |
//placeList.append(AddressTS(address: address[0].compactAddress!, timestamp: geo.timestamp)) | |
//async - sequential reserved - fixed size | |
placeList[index] = AddressTS(address: address[0].compactAddress!, timestamp: geo.timestamp) | |
//TODO: connect with frontend UI. | |
//drawMap(lat:lat,lon:lon,address:address[0]) | |
asyncGroup.leave() | |
} | |
} | |
} | |
} | |
asyncGroup.notify(queue: .main){ | |
print("Finished the loop - find \(geoList.count) addresses") | |
// action complete | |
completionHandler(placeList) | |
} | |
} | |
// UTC time converter | |
func UTC_Converter(unixtime1:Double, unixtime2:Double, timezone:String?=nil) -> String{ | |
// Localization | |
var localTimeZoneAbbreviation: String { return TimeZone.current.abbreviation() ?? "" } // "UTC-4" | |
let date1 = Date(timeIntervalSince1970: unixtime1) | |
let date2 = Date(timeIntervalSince1970: unixtime2) | |
let dateFormatter = DateFormatter() | |
dateFormatter.timeZone = TimeZone(abbreviation: localTimeZoneAbbreviation) //Set timezone that you want: i.e. New York is "UTC-4" | |
// Overwrite | |
if (timezone != nil) { | |
dateFormatter.timeZone = TimeZone(abbreviation: timezone ?? "") | |
} | |
dateFormatter.locale = NSLocale.current | |
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" //Specify your format that you want | |
let strDate1 = dateFormatter.string(from: date1) | |
let strDate2 = dateFormatter.string(from: date2) | |
return (strDate1 + " ~ " + strDate2) | |
} | |
// Step2: aggregate address result - from timestamp to period | |
func aggregateAddressList(addressTSList:[AddressTS], completionHandler:@escaping([AddressPeriod]) -> ()){ | |
var AddressPeriodList: [AddressPeriod] = [] //List of AddressPeriod | |
var start: Double = 0 // UTC start | |
var end: Double = 0 // UTC end | |
var address = "" // address pointer | |
for (index, address_ts) in addressTSList.enumerated(){ | |
if index == 0 { // first one | |
start = address_ts.timestamp | |
end = address_ts.timestamp | |
address = address_ts.address | |
} | |
else if address_ts.address != address { | |
//append - dynamic size | |
//logic: same location + 10min | |
if start == end { | |
AddressPeriodList.append(AddressPeriod(address:address,period: UTC_Converter(unixtime1:start,unixtime2:(start+600)))) | |
} else { | |
AddressPeriodList.append(AddressPeriod(address:address,period: UTC_Converter(unixtime1:start,unixtime2:end))) | |
} | |
//update | |
start = address_ts.timestamp | |
end = address_ts.timestamp | |
address = address_ts.address | |
if index == (addressTSList.count-1){ // last one - unique, add extra | |
//logic: same location + 10min | |
AddressPeriodList.append(AddressPeriod(address:address_ts.address, period: UTC_Converter(unixtime1:start,unixtime2:(start+600)))) | |
} | |
} else {//move on | |
end = address_ts.timestamp | |
if index == (addressTSList.count-1){ // last one - same, add extra | |
//logic: same location + 10min | |
if start == end { | |
AddressPeriodList.append(AddressPeriod(address:address, period: UTC_Converter(unixtime1:start,unixtime2:(start+600)))) | |
} else { | |
AddressPeriodList.append(AddressPeriod(address:address, period: UTC_Converter(unixtime1:start,unixtime2:end))) | |
} | |
} | |
} | |
} | |
completionHandler(AddressPeriodList) | |
} | |
//Test - Type 1 input | |
batchReserseGeocodeLocation(geoList: geoList, completionHandler: { addressTSList in | |
sleep(1) | |
do{ | |
try print("-------------------\nAddressTSList: \((String(data: JSONEncoder().encode(addressTSList), encoding: .utf8)!))") | |
}catch{ print(error) } | |
aggregateAddressList(addressTSList:addressTSList, completionHandler: { addressPeriodList in | |
sleep(1) | |
do{ | |
try print("-------------------\nFinal AddressPeriodList: \((String(data: JSONEncoder().encode(addressPeriodList), encoding: .utf8)!))") | |
}catch{ print(error) } | |
}) | |
}) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
[{"lat":40.75,"lon":-73.980000000000004,"timestamp":1587749400},{"lat":40.75,"lon":-73.980000000000004,"timestamp":1587751200},{"lat":40.759999999999998,"lon":-73.989999999999995,"timestamp":1587752000},{"lat":40.759999999999998,"lon":-73.989999999999995,"timestamp":1587762000},{"lat":40.770000000000003,"lon":-74,"timestamp":1587770000}]
[__lldb_expr_208.Geo(lat: 40.75, lon: -73.98, timestamp: 1587749400.0), __lldb_expr_208.Geo(lat: 40.75, lon: -73.98, timestamp: 1587751200.0), __lldb_expr_208.Geo(lat: 40.76, lon: -73.99, timestamp: 1587752000.0), __lldb_expr_208.Geo(lat: 40.76, lon: -73.99, timestamp: 1587762000.0), __lldb_expr_208.Geo(lat: 40.77, lon: -74.0, timestamp: 1587770000.0)]
1587749400.0
Address 0 : [35 E 38th St, 35 E 38th St, New York, NY 10016, United States @ <+40.75000000,-73.98000000> +/- 100.00m, region CLCircularRegion (identifier:'<+40.75005825,-73.97999890> radius 29.18', center:<+40.75005825,-73.97999890>, radius:29.18m)]
Address 2 : [341 W 45th St, 341 W 45th St, New York, NY 10036, United States @ <+40.76000000,-73.99000000> +/- 100.00m, region CLCircularRegion (identifier:'<+40.76011330,-73.99006390> radius 29.76', center:<+40.76011330,-73.99006390>, radius:29.76m)]
Address 1 : [35 E 38th St, 35 E 38th St, New York, NY 10016, United States @ <+40.75000000,-73.98000000> +/- 100.00m, region CLCircularRegion (identifier:'<+40.75005825,-73.97999890> radius 29.18', center:<+40.75005825,-73.97999890>, radius:29.18m)]
Address 4 : [Hudson River, Hudson River, New York, NY, United States @ <+40.77000000,-74.00000000> +/- 100.00m, region CLCircularRegion (identifier:'<+40.76197890,-74.00707395> radius 7528.16', center:<+40.76197890,-74.00707395>, radius:7528.16m)]
Address 3 : [341 W 45th St, 341 W 45th St, New York, NY 10036, United States @ <+40.76000000,-73.99000000> +/- 100.00m, region CLCircularRegion (identifier:'<+40.76011330,-73.99006390> radius 29.76', center:<+40.76011330,-73.99006390>, radius:29.76m)]
Finished the loop - find 5 addresses
AddressTSList: [{"address":"35 E 38th St, E 38th St, New York, United States","timestamp":1587749400},{"address":"35 E 38th St, E 38th St, New York, United States","timestamp":1587751200},{"address":"341 W 45th St, W 45th St, New York, United States","timestamp":1587752000},{"address":"341 W 45th St, W 45th St, New York, United States","timestamp":1587762000},{"address":"Hudson River, New York, United States","timestamp":1587770000}]
Final AddressPeriodList: [{"address":"35 E 38th St, E 38th St, New York, United States","period":"2020-04-24 13:30:00 ~ 2020-04-24 14:00:00"},{"address":"341 W 45th St, W 45th St, New York, United States","period":"2020-04-24 14:13:20 ~ 2020-04-24 17:00:00"},{"address":"Hudson River, New York, United States","period":"2020-04-24 19:13:20 ~ 2020-04-24 19:23:20"}]