@capgo/nativegeocoder 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,242 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ struct NativeGeocoderResult: Encodable {
5
+ var latitude: String?
6
+ var longitude: String?
7
+ var countryCode: String?
8
+ var countryName: String?
9
+ var postalCode: String?
10
+ var administrativeArea: String?
11
+ var subAdministrativeArea: String?
12
+ var locality: String?
13
+ var subLocality: String?
14
+ var thoroughfare: String?
15
+ var subThoroughfare: String?
16
+ var areasOfInterest: [String]?
17
+ }
18
+
19
+ struct NativeGeocoderError {
20
+ var message: String
21
+ }
22
+
23
+ struct NativeGeocoderOptions: Decodable {
24
+ var useLocale: Bool = true
25
+ var defaultLocale: String?
26
+ var maxResults: Int = 1
27
+ }
28
+
29
+ @objc public class NativeGeocoder: NSObject {
30
+
31
+ typealias ReverseGeocodeCompletionHandler = ([NativeGeocoderResult]?, NativeGeocoderError?) -> Void
32
+ typealias ForwardGeocodeCompletionHandler = ([NativeGeocoderResult]?, NativeGeocoderError?) -> Void
33
+ private static let MAX_RESULTS_COUNT = 5
34
+
35
+ // MARK: - REVERSE GEOCODE
36
+ @objc(reverseGeocode:) func reverseGeocode(latitude: Double, longitude: Double, call: CAPPluginCall) {
37
+ if (CLGeocoder().isGeocoding) {
38
+ call.reject("Geocoder is busy. Please try again later.")
39
+ return
40
+ }
41
+
42
+ let location = CLLocation(latitude: latitude, longitude: longitude)
43
+ var options = NativeGeocoderOptions(useLocale: true, defaultLocale: nil, maxResults: 1)
44
+ options.useLocale = call.getBoolean("useLocale") ?? true
45
+ options.defaultLocale = call.getString()
46
+ options.maxResults = call.getNumber() ?? 1
47
+
48
+ reverseGeocodeLocationHandler(location, options: options, completionHandler: { [weak self] (resultObj, error) in
49
+ if let error = error {
50
+ call.reject(error.message)
51
+ } else {
52
+ if let encodedResult = try? JSONEncoder().encode(resultObj),
53
+ let result = try? JSONSerialization.jsonObject(with: encodedResult, options: .allowFragments) as? [Dictionary<String,Any>] {
54
+ call.resolve(result)
55
+ } else {
56
+ call.reject("Invalid JSON result")
57
+ }
58
+ }
59
+ })
60
+ }
61
+
62
+ private func reverseGeocodeLocationHandler(_ location: CLLocation, options: NativeGeocoderOptions, completionHandler: @escaping ReverseGeocodeCompletionHandler) {
63
+ let geocoderOptions = getNativeGeocoderOptions(from: options)
64
+
65
+ if #available(iOS 11, *) {
66
+ var locale: Locale?
67
+ if let defaultLocaleString = geocoderOptions.defaultLocale {
68
+ locale = Locale.init(identifier: defaultLocaleString)
69
+ } else if (geocoderOptions.useLocale == false) {
70
+ locale = Locale.init(identifier: "en_US")
71
+ }
72
+
73
+ CLGeocoder().reverseGeocodeLocation(location, preferredLocale: locale, completionHandler: { [weak self] (placemarks, error) in
74
+ self?.createReverseGeocodeResult(placemarks, error, maxResults: geocoderOptions.maxResults, completionHandler: { (resultObj, error) in
75
+ completionHandler(resultObj, error)
76
+ })
77
+ })
78
+ } else {
79
+ // fallback for < iOS 11
80
+ CLGeocoder().reverseGeocodeLocation(location, completionHandler: { [weak self] (placemarks, error) in
81
+ self?.createReverseGeocodeResult(placemarks, error, maxResults: geocoderOptions.maxResults, completionHandler: { (resultObj, error) in
82
+ completionHandler(resultObj, error)
83
+ })
84
+ })
85
+ }
86
+ }
87
+
88
+ private func createReverseGeocodeResult(_ placemarks: [CLPlacemark]?, _ error: Error?, maxResults: Int, completionHandler: @escaping ReverseGeocodeCompletionHandler) {
89
+ guard error == nil else {
90
+ completionHandler(nil, NativeGeocoderError(message: "CLGeocoder:reverseGeocodeLocation Error"))
91
+ return
92
+ }
93
+
94
+ if let placemarks = placemarks {
95
+ let maxResultObjects = placemarks.count >= maxResults ? maxResults : placemarks.count
96
+ var resultObj = [NativeGeocoderResult]()
97
+
98
+ for i in 0..<maxResultObjects {
99
+ // https://developer.apple.com/documentation/corelocation/clplacemark
100
+ var latitude = ""
101
+ if let lat = placemarks[i].location?.coordinate.latitude {
102
+ latitude = "\(lat)"
103
+ }
104
+ var longitude = ""
105
+ if let lon = placemarks[i].location?.coordinate.longitude {
106
+ longitude = "\(lon)"
107
+ }
108
+ let placemark = NativeGeocoderResult(
109
+ latitude: latitude,
110
+ longitude: longitude,
111
+ countryCode: placemarks[i].isoCountryCode ?? "",
112
+ countryName: placemarks[i].country ?? "",
113
+ postalCode: placemarks[i].postalCode ?? "",
114
+ administrativeArea: placemarks[i].administrativeArea ?? "",
115
+ subAdministrativeArea: placemarks[i].subAdministrativeArea ?? "",
116
+ locality: placemarks[i].locality ?? "",
117
+ subLocality: placemarks[i].subLocality ?? "",
118
+ thoroughfare: placemarks[i].thoroughfare ?? "",
119
+ subThoroughfare: placemarks[i].subThoroughfare ?? "",
120
+ areasOfInterest: placemarks[i].areasOfInterest ?? []
121
+ )
122
+ resultObj.append(placemark)
123
+ }
124
+
125
+ completionHandler(resultObj, nil)
126
+ }
127
+ else {
128
+ completionHandler(nil, NativeGeocoderError(message: "Cannot get an address"))
129
+ }
130
+ }
131
+
132
+
133
+ // MARK: - FORWARD GEOCODE
134
+ @objc(forwardGeocode:)func forwardGeocode(address: String, call: CAPPluginCall) {
135
+
136
+ if (CLGeocoder().isGeocoding) {
137
+ call.reject("Geocoder is busy. Please try again later.")
138
+ return
139
+ }
140
+
141
+ var options = NativeGeocoderOptions(useLocale: true, defaultLocale: nil, maxResults: 1)
142
+ options.useLocale = call.getBoolean("useLocale") ?? true
143
+ options.defaultLocale = call.getString()
144
+ options.maxResults = call.getNumber() ?? 1
145
+
146
+ forwardGeocodeHandler(address, options: options, completionHandler: { [weak self] (resultObj, error) in
147
+ if let error = error {
148
+ call.reject(error.message)
149
+ } else {
150
+ if let encodedResult = try? JSONEncoder().encode(resultObj),
151
+ let result = try? JSONSerialization.jsonObject(with: encodedResult, options: .allowFragments) as? [Dictionary<String,Any>] {
152
+ call.resolve(result)
153
+ } else {
154
+ call.reject("Invalid JSON result")
155
+ }
156
+ }
157
+ })
158
+ }
159
+
160
+ func forwardGeocodeHandler(_ address: String, options: NativeGeocoderOptions, completionHandler: @escaping ForwardGeocodeCompletionHandler) {
161
+ let geocoderOptions = getNativeGeocoderOptions(from: options)
162
+
163
+ if #available(iOS 11, *) {
164
+ var locale: Locale?
165
+ if let defaultLocaleString = geocoderOptions.defaultLocale {
166
+ locale = Locale.init(identifier: defaultLocaleString)
167
+ } else if (geocoderOptions.useLocale == false) {
168
+ locale = Locale.init(identifier: "en_US")
169
+ }
170
+
171
+ CLGeocoder().geocodeAddressString(address, in: nil, preferredLocale: locale, completionHandler: { [weak self] (placemarks, error) in
172
+ self?.createForwardGeocodeResult(placemarks, error, maxResults: geocoderOptions.maxResults, completionHandler: { (resultObj, error) in
173
+ completionHandler(resultObj, error)
174
+ })
175
+ })
176
+ } else {
177
+ // fallback for < iOS 11
178
+ CLGeocoder().geocodeAddressString(address, completionHandler: { [weak self] (placemarks, error) in
179
+ self?.createForwardGeocodeResult(placemarks, error, maxResults: geocoderOptions.maxResults, completionHandler: { (resultObj, error) in
180
+ completionHandler(resultObj, error)
181
+ })
182
+ })
183
+ }
184
+ }
185
+
186
+ private func createForwardGeocodeResult(_ placemarks: [CLPlacemark]?, _ error: Error?, maxResults: Int, completionHandler: @escaping ForwardGeocodeCompletionHandler) {
187
+ guard error == nil else {
188
+ completionHandler(nil, NativeGeocoderError(message: "CLGeocoder:geocodeAddressString Error"))
189
+ return
190
+ }
191
+
192
+ if let placemarks = placemarks {
193
+ let maxResultObjects = placemarks.count >= maxResults ? maxResults : placemarks.count
194
+ var resultObj = [NativeGeocoderResult]()
195
+
196
+ for i in 0..<maxResultObjects {
197
+ if let latitude = placemarks[i].location?.coordinate.latitude,
198
+ let longitude = placemarks[i].location?.coordinate.longitude {
199
+
200
+ // https://developer.apple.com/documentation/corelocation/clplacemark
201
+ let placemark = NativeGeocoderResult(
202
+ latitude: "\(latitude)",
203
+ longitude: "\(longitude)",
204
+ countryCode: placemarks[i].isoCountryCode ?? "",
205
+ countryName: placemarks[i].country ?? "",
206
+ postalCode: placemarks[i].postalCode ?? "",
207
+ administrativeArea: placemarks[i].administrativeArea ?? "",
208
+ subAdministrativeArea: placemarks[i].subAdministrativeArea ?? "",
209
+ locality: placemarks[i].locality ?? "",
210
+ subLocality: placemarks[i].subLocality ?? "",
211
+ thoroughfare: placemarks[i].thoroughfare ?? "",
212
+ subThoroughfare: placemarks[i].subThoroughfare ?? "",
213
+ areasOfInterest: placemarks[i].areasOfInterest ?? []
214
+ )
215
+ resultObj.append(placemark)
216
+ }
217
+ }
218
+
219
+ if (resultObj.count == 0) {
220
+ completionHandler(nil, NativeGeocoderError(message: "Cannot get latitude and/or longitude"))
221
+ } else {
222
+ completionHandler(resultObj, nil)
223
+ }
224
+ }
225
+ else {
226
+ completionHandler(nil, NativeGeocoderError(message: "Cannot find a location"))
227
+ }
228
+ }
229
+
230
+ // MARK: - Helper
231
+ private func getNativeGeocoderOptions(from options: NativeGeocoderOptions) -> NativeGeocoderOptions {
232
+ var geocoderOptions = NativeGeocoderOptions()
233
+ geocoderOptions.useLocale = options.useLocale
234
+ geocoderOptions.defaultLocale = options.defaultLocale
235
+ if (options.maxResults > 0) {
236
+ geocoderOptions.maxResults = options.maxResults > NativeGeocoder.MAX_RESULTS_COUNT ? NativeGeocoder.MAX_RESULTS_COUNT : options.maxResults
237
+ } else {
238
+ geocoderOptions.maxResults = 1
239
+ }
240
+ return geocoderOptions
241
+ }
242
+ }
@@ -0,0 +1,10 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ //! Project version number for Plugin.
4
+ FOUNDATION_EXPORT double PluginVersionNumber;
5
+
6
+ //! Project version string for Plugin.
7
+ FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8
+
9
+ // In this header, you should import all the public headers of your framework using statements like #import <Plugin/PublicHeader.h>
10
+
@@ -0,0 +1,9 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <Capacitor/Capacitor.h>
3
+
4
+ // Define the plugin using the CAP_PLUGIN Macro, and
5
+ // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6
+ CAP_PLUGIN(NativeGeocoderPlugin, "NativeGeocoder",
7
+ CAP_PLUGIN_METHOD(reverseGeocode, CAPPluginReturnPromise);
8
+ CAP_PLUGIN_METHOD(forwardGeocode, CAPPluginReturnPromise);
9
+ )
@@ -0,0 +1,31 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Please read the Capacitor iOS Plugin Development Guide
6
+ * here: https://capacitorjs.com/docs/plugins/ios
7
+ */
8
+ @objc(NativeGeocoderPlugin)
9
+ public class NativeGeocoderPlugin: CAPPlugin {
10
+ private let implementation = NativeGeocoder()
11
+
12
+ @objc func reverseGeocode(_ call: CAPPluginCall) {
13
+ guard let addressString = call.getString("addressString") else {
14
+ call.reject("Missing addressString")
15
+ return
16
+ }
17
+ implementation.reverseGeocode(addressString: addressString, call: call)
18
+ }
19
+
20
+ @objc func forwardGeocode(_ call: CAPPluginCall) {
21
+ guard let latitude = call.getNumber("latitude") else {
22
+ call.reject("Missing latitude")
23
+ return
24
+ }
25
+ guard let longitude = call.getNumber("longitude") else {
26
+ call.reject("Missing longitude")
27
+ return
28
+ }
29
+ implementation.forwardGeocode(latitude: latitude, longitude: longitude, call: call)
30
+ }
31
+ }
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@capgo/nativegeocoder",
3
+ "version": "0.0.1",
4
+ "description": "Capacitor plugin for native forward and reverse geocoding",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "CapgoNativegeocoder.podspec"
15
+ ],
16
+ "author": "Martin Donadieu",
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Cap-go/capacitor-nativegeocoder.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/Cap-go/capacitor-nativegeocoder/issues"
24
+ },
25
+ "keywords": [
26
+ "capacitor",
27
+ "plugin",
28
+ "geocoder",
29
+ "native"
30
+ ],
31
+ "scripts": {
32
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
33
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin && cd ..",
34
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
35
+ "verify:web": "npm run build",
36
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
37
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
38
+ "eslint": "eslint . --ext ts",
39
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
40
+ "swiftlint": "node-swiftlint",
41
+ "docgen": "docgen --api NativeGeocoderPlugin --output-readme README.md --output-json dist/docs.json",
42
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
43
+ "clean": "rimraf ./dist",
44
+ "watch": "tsc --watch",
45
+ "prepublishOnly": "npm run build"
46
+ },
47
+ "devDependencies": {
48
+ "@capacitor/android": "^3.0.0",
49
+ "@capacitor/core": "^3.0.0",
50
+ "@capacitor/docgen": "^0.0.18",
51
+ "@capacitor/ios": "^3.0.0",
52
+ "@ionic/eslint-config": "^0.3.0",
53
+ "@ionic/prettier-config": "^1.0.1",
54
+ "@ionic/swiftlint-config": "^1.1.2",
55
+ "eslint": "^7.11.0",
56
+ "prettier": "~2.2.0",
57
+ "prettier-plugin-java": "~1.0.0",
58
+ "rimraf": "^3.0.2",
59
+ "rollup": "^2.32.0",
60
+ "swiftlint": "^1.0.1",
61
+ "typescript": "~4.0.3"
62
+ },
63
+ "peerDependencies": {
64
+ "@capacitor/core": "^3.0.0"
65
+ },
66
+ "prettier": "@ionic/prettier-config",
67
+ "swiftlint": "@ionic/swiftlint-config",
68
+ "eslintConfig": {
69
+ "extends": "@ionic/eslint-config/recommended"
70
+ },
71
+ "capacitor": {
72
+ "ios": {
73
+ "src": "ios"
74
+ },
75
+ "android": {
76
+ "src": "android"
77
+ }
78
+ }
79
+ }