Pääsivulle | TIETOTEKNIIKAN PÄÄSIVULLE
Koordinaattien sijaintimaa
Seuraavassa esitetään funktio etsiMaa(..), joka tutkii ovatko
annetut koordinaatit Suomessa vai ei. Tietoa ei kysellä
ensisijaisesti netistä, vaan verrataan koordinaatteja Suomen
sisäpuolen karkeasti määritteleviin koordinaattiarvoihin, ja
toisaalta suorakaiteeseen jonka sisään Suomi mahtuu kokonaan
Lopussa esitetään luokka NettiKysely, jota funktio etsiMaa
tarvitsee. Lisäksi siellä on luokka Reachability jonka avulla
voidaan tehokkaasti selvittää onko meillä nettiyhteyttä vai ei.
//--------------------------------------------
// 2.2.2018 M27
// Missä maassa koordinaatit ovat
// Output "Suomi", "Muu"
// "" ei selvinnyt eli joko ei
nettiä tai googlen palvelu ei vastannut
// Maa selvitetään ensisijaisesti oheisten koordinaattien avulla:
// - Suomi suorakaiteen sisällä: ulkopuolinen alue on "Muu"
// - Suomen sisäpuoli (alla taulukko luvut): sisäpuolinen alue on
"Suomi"
// Em. koordinaattien välinen alue tutkitaan netistä
Google-palvelun avulla: onko inputtina
// olevat koordinaatit Suomea vai ei, tulos "Suomi", "Muu" tai ""
// Vastaava Openstreet-kysely on epäluotettavampi (usein hidas ja
tulee timeout)
// Input lati ja longi oltava oheisessa muodossa:
// let maaz = Meka.etsiMaa(lati: "60.582535", longi: "25.682804")
// Täällä kutsutaan funktiota jossa on trailing closure ja kutsuva
koodi jatkaa ohi
class func etsiMaa(lati laz: String, longi lonz: String) ->
String
{
if laz.isEmpty || lonz.isEmpty
{
print("Meka M27-01: input
tyhjä")
return ""
}
var maaz = ""
// Suomen sisäpuoli on "viipaloitu
vaakasuoraan"
// Vaakasuora latitude-viiva, pystysuorat rajat
logi vasen ja longi oikea:
// vaaka[i], lonvas[i], lonoik[i]
// lati, vaakasuoran viivan vasen reuna
(lonvas), oikea reuna (lonoik)
let luvut =
[
59.77325,
21.80186, 23.58164,
59.92775,
19.94517, 24.72422,
60.15270,
19.45078, 25.95469,
60.34352,
19.36289, 27.65757,
60.54943,
19.91221, 27.75645,
60.68419,
20.97788, 27.96519,
61.32852,
21.27451, 29.15171,
62.69959,
20.67257, 31.31554,
63.35327,
20.96920, 30.75787,
63.69035,
22.30675, 29.97521,
64.74155,
23.94419, 29.62364,
65.52683,
24.96544, 29.52477,
65.91980,
24.11949, 29.75548,
66.36346,
23.77057, 29.48346,
66.80510,
24.04259, 28.81733,
68.40746,
23.53290, 28.50567,
68.54852,
25.18085, 28.27900,
69.02590,
25.79411, 28.36766,
69.27517,
25.75291, 28.84556,
69.58398,
25.96440, 28.98907,
69.88141,
26.46625, 28.14716
];
var vaaka = [Double]()
var lonvas = [Double]()
var lonoik = [Double]()
var i = 1
for luku in luvut
{
if i == 3
{
lonoik.append(luku)
i = 1
}
else if i == 2
{
lonvas.append(luku)
i = 3
}
else // if i == 1
{
vaaka.append(luku)
i = 2
}
}
if 3 * vaaka.count != luvut.count
{
print("Meka M27-05: taulut
epäonnistuivat")
return ""
}
let latd = Double(laz) ?? 0.0 //
input muutetaan String -> Double
let lond = Double(lonz) ?? 0.0
if (latd < 59.65135 || latd >
70.15173) // Suomi kokonaan tämän suorakaiteen sisällä
{
return "Muu";
}
if (lond < 18.87949 || lond > 31.71152)
{
return "Muu";
}
if (latd >= vaaka[0] && latd <=
vaaka[vaaka.count - 1])
{
for i in 0..<(vaaka.count
- 1)
{
if (latd
>= vaaka[i] && latd < vaaka[i+1])
{
// Kun piste on vaakasuorien vakioviivojen
välissä, tehdään
// uusi vaakaviiva pisteen kautta, päät
lineaarisesti ylä- ja alapuolten päiden suhteen
let kerr = (latd - vaaka[i]) / (vaaka[i+1] -
vaaka[i]);
let lonva = lonvas[i] + (lonvas[i+1] -
lonvas[i]) * kerr;
let lonoi = lonoik[i] + (lonoik[i+1] -
lonoik[i]) * kerr;
if (lond >= lonva && lond <=
lonoi)
{
return "Suomi";
}
}
}
}
// Kun ei löytynyt Suomea eikä varmuudella
Muuta, niin pitää etsiä seuraavaksi netistä
let semaphore = DispatchSemaphore(value: 0)
let nky = NettiKysely() // luokan
määrittely jäljempänä
nky.latiz = laz
nky.longiz = lonz
// Nettikysely vie aikaa, sovellus ei jää
odottamaan, eli homma jatkuu
// tämän sulkuryhmän jälkeen suoraan ja
sulkuryhmä valmistuu "joskus"
// Nyt on jäätävä odottamaan, siksi semaphore
nky.haeData
// This method has a trailing closure
{ tuloz in
if tuloz
== "Virhe" // ei esim. saatu yhteyttä nettiin
{
print("Meka M27-05: maan nettikysely
epäonnistui")
maaz = ""
}
else if
tuloz == "ei Suomi"
{
print("Meka M27-06: ei olla Suomessa")
maaz = "Muu"
}
else
{
print("Meka M27-07: Suomessa ollaan")
maaz = "Suomi"
}
semaphore.signal()
// nyt semaforilta saa jatkaa matkaa
}
semaphore.wait()
// pyydetään prosessia odottamaan semaforille
print("Meka M27-09: Maa = \(maaz)")
return maaz
}
*************************************************************
// Luokassa käsitellään nettiin menevä kysely koordinaattien
maasta Suomi / ei Suomi.
// Funktio haeData() tekee asynkronisen kyselyn
// Kutsujan on hoidettava, että latiz ja longiz saavat arvon
// Jotta allaoleva http-nettikysely onnistuisi, on info.plist
täydennettävä:
// <key>NSAppTransportSecurity</key>
// <dict>
//
<key>NSAllowsArbitraryLoads</key><true/>
// </dict>
class NettiKysely: NSObject
{
var urliz = ""
var latiz = ""
var longiz = ""
var paikkaz = ""
//
----------------------------------------------------
// 14.12.2017 M33
// the completion closure signature is
(NSString) -> ()
// Selvitetään onko (latiz, longiz) Suomessa
vai ei,
// kysytään GoogleMapistä (OpenStreetMap oli
epävarmempi)
// Huom! Kutsuvassa paikassa tarkistettava että
lngz ja latz eivät ole null tai tyhjiä
// Output on "ei Suomi", "Suomi" tai "Virhe"
func haeData(_ completion: @escaping (NSString)
-> ())
{
// Allaoleva Google-palvelu
ei toimi suoraan, vaan tarvitset Googlelta api-tunnuksen
// antamalle heille
luottokorttisi tiedot:
urliz =
"http://maps.googleapis.com/maps/api/geocode/json?latlng=\(latiz),\(longiz)&sensor=true"
// Tämä OpenStreet-palvelu
toimii, jos käyttäjä on identifioitu. Omassa sovelluksessa voi
// käyttää omaa
sähköpostiosoitetta:
urliz =
"https://nominatim.openstreetmap.org/reverse?email=ETUNIMI.SUKUNIMI@byethost.com&format=xml&lat="
+ latiz + "&lon=" +
longiz + "&zoom=18&addressdetails=1";
if let url = URL(string:
urliz)
{
let
session = URLSession.shared // tämä on asynkroninen
let task
= session.dataTask(with: url, completionHandler:
{
data, response, error in
if let data = data, let jsonString =
String(data: data, encoding: String.Encoding.utf8), error == nil
{
let suomiz =
"Suomi"
var tuloz = "ei Suomi"
if jsonString.range(of:
suomiz) != nil
{
tuloz =
"Suomi"
}
else if jsonString.range(of:
"Finland") != nil
{
tuloz =
"Suomi"
}
completion(tuloz as
NSString)
}
else
{
print("Meka M33-01:
error=\(error!.localizedDescription)")
completion("Virhe")
}
})
task.resume()
}
else
{
print("Meka M33-03: url on virheellinen")
completion("Virhe")
}
}
}
*************************************************************
// 30.1.2018
// Tämän avulla voidaan tutkia onko nettiyhteyttä
//
https://stackoverflow.com/questions/25623272/how-to-use-scnetworkreachability-in-swift
// Käyttötapa:
// if !Reachability.shared.isConnectedToNetwork() { // ei nettiä }
import SystemConfiguration
final class Reachability
{
private init () {}
class var shared: Reachability
{
struct Static
{
static
let instance: Reachability = Reachability()
}
return Static.instance
}
func isConnectedToNetwork() -> Bool
{
guard let flags = getFlags()
else
{
return false
}
let isReachable =
flags.contains(.reachable)
let needsConnection =
flags.contains(.connectionRequired)
return (isReachable
&& !needsConnection)
}
private func getFlags() ->
SCNetworkReachabilityFlags?
{
guard let reachability =
ipv4Reachability() ?? ipv6Reachability()
else
{
return nil
}
var flags =
SCNetworkReachabilityFlags()
if
!SCNetworkReachabilityGetFlags(reachability, &flags)
{
return
nil
}
return flags
}
private func ipv6Reachability() ->
SCNetworkReachability?
{
var zeroAddress =
sockaddr_in6()
zeroAddress.sin6_len =
UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin6_family =
sa_family_t(AF_INET6)
return withUnsafePointer(to:
&zeroAddress,
{
$0.withMemoryRebound(to: sockaddr.self, capacity: 1)
{
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
})
}
private func ipv4Reachability() ->
SCNetworkReachability?
{
var zeroAddress =
sockaddr_in()
zeroAddress.sin_len =
UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family =
sa_family_t(AF_INET)
return withUnsafePointer(to:
&zeroAddress,
{
$0.withMemoryRebound(to: sockaddr.self, capacity: 1)
{
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
})
}
}
--------------------------------
(sivua muokattu 8.4.2020)