diff --git a/README.md b/README.md index a21585c..ab3b384 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,14 @@ pickPlace({ enableUserLocation: true, enableGeocoding: true, color: "#FF00FF", + // Range selection (optional) + enableRangeSelection: true, + initialRadius: 2000, + minRadius: 250, + maxRadius: 10000, + radiusColor: '#FF00FF33', + radiusStrokeColor: '#FF00FF', + radiusStrokeWidth: 2, //...etc }) .then(console.log) @@ -133,6 +141,13 @@ pickPlace().then(console.log).catch(console.log); | `enableUserLocation` | `boolean` | current user position button. Requires setup. | `true` | | `enableLargeTitle` | `boolean` | large navigation bar title of the UIViewController. **iOS only** | `true` | | `rejectOnCancel` | `boolean` | Reject and return nothing if the user dismisses the window without selecting a place. | `true` | +| `enableRangeSelection` | `boolean` | Enable draggable radius selection overlay. | `false` | +| `initialRadius` | `number` | Initial radius in meters when range selection is enabled. | `1000` | +| `minRadius` | `number` | Minimum allowed radius in meters. | `100` | +| `maxRadius` | `number` | Maximum allowed radius in meters. | `10000` | +| `radiusColor` | `string` | Fill color of radius circle (falls back to `color` with alpha). | `''` | +| `radiusStrokeColor` | `string` | Stroke color of radius circle (falls back to `color`). | `''` | +| `radiusStrokeWidth` | `number` | Stroke width of radius circle in pixels. | `2` | ### PlacePickerPresentationStyle @@ -166,6 +181,8 @@ pickPlace().then(console.log).catch(console.log); | `coordinate` | `PlacePickerCoordinate` | Selected coordinate. | | `address` | `PlacePickerAddress` | Geocoded address for selected location (if `enableGeocoding`). | | `didCancel` | `boolean` | Indicates if the place picker was canceled without selecting. | +| `radius` | `number` | Selected radius in meters (if range selection enabled). | +| `radiusCoordinates` | `{ center, bounds }` | Center and bounds of the selected area. | ## Contributing diff --git a/android/src/main/java/expo/modules/placepicker/PlacePickerActivity.kt b/android/src/main/java/expo/modules/placepicker/PlacePickerActivity.kt index d475302..125360a 100644 --- a/android/src/main/java/expo/modules/placepicker/PlacePickerActivity.kt +++ b/android/src/main/java/expo/modules/placepicker/PlacePickerActivity.kt @@ -25,6 +25,8 @@ import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.Circle +import com.google.android.gms.maps.model.CircleOptions import java.util.Locale import java.util.concurrent.Executors import java.util.concurrent.ScheduledFuture @@ -40,6 +42,10 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, private lateinit var pinViewAnimation: ObjectAnimator private var mLocationProvider: FusedLocationProviderClient? = null private lateinit var geocoder: Geocoder + private var radiusCircle: Circle? = null + private var currentRadius: Double = 1000.0 + private var radiusHandle: View? = null + private var isDragging: Boolean = false private fun getLocationProvider(): FusedLocationProviderClient { if (mLocationProvider == null) { @@ -64,6 +70,10 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, ) this.title = PlacePickerState.globalOptions.title supportActionBar?.subtitle = "" + if (PlacePickerState.globalOptions.enableRangeSelection) { + currentRadius = PlacePickerState.globalOptions.initialRadius + .coerceIn(PlacePickerState.globalOptions.minRadius, PlacePickerState.globalOptions.maxRadius) + } } private fun gatherViews() { @@ -104,6 +114,9 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, ), 15F ) ) + if (PlacePickerState.globalOptions.enableRangeSelection) { + setupRadiusSelection() + } } override fun onCameraMoveStarted(reason: Int) { @@ -120,6 +133,10 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, if (!PlacePickerState.globalOptions.enableGeocoding) { pinViewAnimation.reverse() animationIsUp = false + if (PlacePickerState.globalOptions.enableRangeSelection) { + updateRadiusHandlePosition() + updateCircle() + } return } mapMoveTask?.cancel(true) @@ -134,10 +151,18 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, supportActionBar?.subtitle = lastAddress?.featureName ?: "Unknown location" pinViewAnimation.reverse() animationIsUp = false + if (PlacePickerState.globalOptions.enableRangeSelection) { + updateRadiusHandlePosition() + updateCircle() + } } catch (e: Exception) { supportActionBar?.subtitle = "" pinViewAnimation.reverse() animationIsUp = false + if (PlacePickerState.globalOptions.enableRangeSelection) { + updateRadiusHandlePosition() + updateCircle() + } } } }, 1, TimeUnit.SECONDS) @@ -294,6 +319,9 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, country = add?.countryName ?: "" } } + if (PlacePickerState.globalOptions.enableRangeSelection) { + appendRadiusData() + } if (item.itemId == R.id.action_done) { PlacePickerState.globalResult.didCancel = false PlacePickerState.globalPromise?.resolve(PlacePickerState.globalResult) @@ -314,4 +342,121 @@ class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback, return true } + // Radius helpers + private fun setupRadiusSelection() { + setupRadiusHandle() + updateCircle() + updateRadiusHandlePosition() + } + + private fun updateCircle() { + radiusCircle?.remove() + radiusCircle = mMap.addCircle( + CircleOptions() + .center(mMap.cameraPosition.target) + .radius(currentRadius) + .fillColor(parseFillColor()) + .strokeColor(parseStrokeColor()) + .strokeWidth(PlacePickerState.globalOptions.radiusStrokeWidth.toFloat()) + ) + } + + private fun setupRadiusHandle() { + if (radiusHandle != null) return + val handle = View(this) + val size = 56 + handle.layoutParams = android.view.ViewGroup.LayoutParams(size, size) + handle.background = pinView.background.mutate() + handle.background.setTint(Color.parseColor(PlacePickerState.globalOptions.color)) + handle.setOnTouchListener { _, event -> + when (event.action) { + android.view.MotionEvent.ACTION_DOWN -> { + isDragging = true + true + } + android.view.MotionEvent.ACTION_MOVE -> { + if (!isDragging) return@setOnTouchListener true + val centerScreen = mMap.projection.toScreenLocation(mMap.cameraPosition.target) + val dx = event.rawX - centerScreen.x + val dy = event.rawY - centerScreen.y + val distancePx = kotlin.math.sqrt(dx*dx + dy*dy) + val metersPerPixel = metersPerPixelAtLatitude(mMap.cameraPosition.target.latitude) + val newRadius = (distancePx * metersPerPixel).toDouble() + .coerceIn(PlacePickerState.globalOptions.minRadius, PlacePickerState.globalOptions.maxRadius) + currentRadius = newRadius + updateRadiusHandlePosition() + true + } + android.view.MotionEvent.ACTION_UP, android.view.MotionEvent.ACTION_CANCEL -> { + isDragging = false + updateCircle() + true + } + else -> false + } + } + addContentView(handle, handle.layoutParams) + radiusHandle = handle + } + + private fun updateRadiusHandlePosition() { + val handle = radiusHandle ?: return + val centerGeo = mMap.cameraPosition.target + val eastGeo = offsetCoordinate(centerGeo.latitude, centerGeo.longitude, currentRadius, 0.0) + val edgeScreen = mMap.projection.toScreenLocation(eastGeo) + handle.x = edgeScreen.x - handle.width / 2f + handle.y = edgeScreen.y - handle.height / 2f + } + + private fun metersPerPixelAtLatitude(lat: Double): Float { + val earthCircumference = 40075016.686 + val zoom = mMap.cameraPosition.zoom.toDouble() + val scale = Math.pow(2.0, zoom) + val metersPerPixel = (Math.cos(Math.toRadians(lat)) * earthCircumference) / (256.0 * scale) + return metersPerPixel.toFloat() + } + + private fun parseFillColor(): Int { + val color = if (PlacePickerState.globalOptions.radiusColor.isNotEmpty()) PlacePickerState.globalOptions.radiusColor else PlacePickerState.globalOptions.color + val base = Color.parseColor(color) + return Color.argb(64, Color.red(base), Color.green(base), Color.blue(base)) + } + + private fun parseStrokeColor(): Int { + val color = if (PlacePickerState.globalOptions.radiusStrokeColor.isNotEmpty()) PlacePickerState.globalOptions.radiusStrokeColor else PlacePickerState.globalOptions.color + return Color.parseColor(color) + } + + private fun appendRadiusData() { + PlacePickerState.globalResult.radius = currentRadius + val center = PlacePickerCoordinate().apply { + latitude = mMap.cameraPosition.target.latitude + longitude = mMap.cameraPosition.target.longitude + } + val ne = offsetCoordinate(center.latitude, center.longitude, currentRadius, currentRadius) + val sw = offsetCoordinate(center.latitude, center.longitude, -currentRadius, -currentRadius) + val bounds = BoundsCoordinates().apply { + northeast = PlacePickerCoordinate().apply { + latitude = ne.latitude + longitude = ne.longitude + } + southwest = PlacePickerCoordinate().apply { + latitude = sw.latitude + longitude = sw.longitude + } + } + PlacePickerState.globalResult.radiusCoordinates = RadiusCoordinates().apply { + this.center = center + this.bounds = bounds + } + } + + private fun offsetCoordinate(lat: Double, lon: Double, metersEast: Double, metersNorth: Double): LatLng { + val earthRadius = 6378137.0 + val dLat = metersNorth / earthRadius + val dLon = metersEast / (earthRadius * kotlin.math.cos(Math.toRadians(lat))) + val newLat = lat + Math.toDegrees(dLat) + val newLon = lon + Math.toDegrees(dLon) + return LatLng(newLat, newLon) + } } diff --git a/android/src/main/java/expo/modules/placepicker/Types/PlacePickerOptions.kt b/android/src/main/java/expo/modules/placepicker/Types/PlacePickerOptions.kt index b272bd4..7e181c7 100644 --- a/android/src/main/java/expo/modules/placepicker/Types/PlacePickerOptions.kt +++ b/android/src/main/java/expo/modules/placepicker/Types/PlacePickerOptions.kt @@ -39,4 +39,25 @@ class PlacePickerOptions : Record { @Field val rejectOnCancel: Boolean = true + + @Field + val enableRangeSelection: Boolean = false + + @Field + val initialRadius: Double = 1000.0 + + @Field + val minRadius: Double = 100.0 + + @Field + val maxRadius: Double = 10000.0 + + @Field + val radiusColor: String = "" + + @Field + val radiusStrokeColor: String = "" + + @Field + val radiusStrokeWidth: Double = 2.0 } diff --git a/android/src/main/java/expo/modules/placepicker/Types/PlacePickerResult.kt b/android/src/main/java/expo/modules/placepicker/Types/PlacePickerResult.kt index 25217ae..2471c12 100644 --- a/android/src/main/java/expo/modules/placepicker/Types/PlacePickerResult.kt +++ b/android/src/main/java/expo/modules/placepicker/Types/PlacePickerResult.kt @@ -12,4 +12,24 @@ class PlacePickerResult : Record { @Field var didCancel: Boolean? = null + + @Field + var radius: Double? = null + + @Field + var radiusCoordinates: RadiusCoordinates? = null +} + +class BoundsCoordinates : Record { + @Field + var northeast: PlacePickerCoordinate? = null + @Field + var southwest: PlacePickerCoordinate? = null +} + +class RadiusCoordinates : Record { + @Field + var center: PlacePickerCoordinate? = null + @Field + var bounds: BoundsCoordinates? = null } diff --git a/ios/PlacePickerViewController.swift b/ios/PlacePickerViewController.swift index f4beb49..0865808 100644 --- a/ios/PlacePickerViewController.swift +++ b/ios/PlacePickerViewController.swift @@ -27,6 +27,11 @@ class PlacePickerViewController: UIViewController { private var mapMoveDebounceTimer:Timer? private let geocoder = CLGeocoder() private let locationManager = CLLocationManager() + private var circleOverlay: MKCircle? + private var currentRadius: Double = 1000 + private var radiusHandle: UIView? + private var radiusPanGesture: UIPanGestureRecognizer? + private var didSetInitialRegion: Bool = false // MARK: - Inits init(_ options: PlacePickerOptions,_ promise: Promise) { @@ -241,11 +246,26 @@ class PlacePickerViewController: UIViewController { completer.delegate = self } setupViews() + if (options.enableRangeSelection) { + currentRadius = max(options.minRadius, min(options.maxRadius, options.initialRadius)) + setupRadiusSelection() + } } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - mapView.centerCoordinate = CLLocationCoordinate2D(latitude: options.initialCoordinates.latitude, longitude: options.initialCoordinates.longitude) - mapView.delegate = self + if !didSetInitialRegion { + let region = MKCoordinateRegion( + center: CLLocationCoordinate2D(latitude: options.initialCoordinates.latitude, longitude: options.initialCoordinates.longitude), + latitudinalMeters: 1000, + longitudinalMeters: 1000 + ) + mapView.setRegion(region, animated: false) + mapView.delegate = self + didSetInitialRegion = true + } + if options.enableRangeSelection { + updateRadiusHandlePosition() + } } override func viewSafeAreaInsetsDidChange() { super.viewSafeAreaInsetsDidChange() @@ -272,10 +292,35 @@ class PlacePickerViewController: UIViewController { promise?.reject("cancel", "User cancel the operation and `rejectOnCancel` is enabled") } } else { + let center = mapView.centerCoordinate + let ne = coordinateOffset(from: center, metersEast: currentRadius, metersNorth: currentRadius) + let sw = coordinateOffset(from: center, metersEast: -currentRadius, metersNorth: -currentRadius) let result = PlacePickerResult( coordinate: .init(wrappedValue: PlacePickerCoordinate(latitude: .init(wrappedValue: mapView.centerCoordinate.latitude), longitude: .init(wrappedValue: mapView.centerCoordinate.longitude))), address: .init(wrappedValue: PlacePickerAddress(with: self.lastLocation)), - didCancel: .init(wrappedValue: true)) + didCancel: .init(wrappedValue: true), + radius: .init(wrappedValue: currentRadius), + radiusCoordinates: .init( + wrappedValue: RadiusCoordinates( + center: .init( + wrappedValue: PlacePickerCoordinate( + latitude: .init(wrappedValue: mapView.centerCoordinate.latitude), + longitude: .init(wrappedValue: mapView.centerCoordinate.longitude))), + bounds: .init( + wrappedValue: BoundsCoordinates( + northeast: .init( + wrappedValue: PlacePickerCoordinate( + latitude: .init( + wrappedValue: ne.latitude), + longitude: .init( + wrappedValue: ne.longitude))), + southwest: .init( + wrappedValue: PlacePickerCoordinate( + latitude: .init( + wrappedValue: sw.latitude), + longitude: .init( + wrappedValue: sw.longitude))))))) + ) promise?.resolve(result) } promise = nil @@ -284,10 +329,34 @@ class PlacePickerViewController: UIViewController { } } @objc private func finalizePicker() { + let center = mapView.centerCoordinate + let ne = coordinateOffset(from: center, metersEast: currentRadius, metersNorth: currentRadius) + let sw = coordinateOffset(from: center, metersEast: -currentRadius, metersNorth: -currentRadius) let result = PlacePickerResult( coordinate: .init(wrappedValue: PlacePickerCoordinate(latitude: .init(wrappedValue: mapView.centerCoordinate.latitude), longitude: .init(wrappedValue: mapView.centerCoordinate.longitude))), address: .init(wrappedValue: PlacePickerAddress(with: self.lastLocation)), - didCancel: .init(wrappedValue: false)) + didCancel: .init(wrappedValue: false), + radius: .init(wrappedValue: currentRadius), + radiusCoordinates: .init( + wrappedValue: RadiusCoordinates( + center: .init( + wrappedValue: PlacePickerCoordinate( + latitude: .init(wrappedValue: mapView.centerCoordinate.latitude), + longitude: .init(wrappedValue: mapView.centerCoordinate.longitude))), + bounds: .init( + wrappedValue: BoundsCoordinates( + northeast: .init( + wrappedValue: PlacePickerCoordinate( + latitude: .init( + wrappedValue: ne.latitude), + longitude: .init( + wrappedValue: ne.longitude))), + southwest: .init( + wrappedValue: PlacePickerCoordinate( + latitude: .init( + wrappedValue: sw.latitude), + longitude: .init( + wrappedValue: sw.longitude)))))))) promise?.resolve(result) promise = nil DispatchQueue.main.async { @@ -308,28 +377,36 @@ class PlacePickerViewController: UIViewController { startPinAnimation() } private func mapDidMove() { - if (options.enableGeocoding) { - setLoading(true) - geocoder.reverseGeocodeLocation(CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude), preferredLocale: Locale(identifier: options.locale)) { location, error in - if let _ = error { + mapMoveDebounceTimer?.invalidate() + mapMoveDebounceTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { + [weak self] _ in guard let self = self else { return } + if (options.enableGeocoding) { + setLoading(true) + geocoder.reverseGeocodeLocation(CLLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude), preferredLocale: Locale(identifier: options.locale)) { location, error in + if let _ = error { + self.setLoading(false) + self.endPinAnimation() + self.lastLocation = nil + self.navigationItem.searchController?.searchBar.placeholder = self.options.searchPlaceholder + return + } + self.lastLocation = location?.first + if let name = location?.first?.name { + self.navigationItem.searchController?.searchBar.placeholder = name + } else { + self.navigationItem.searchController?.searchBar.placeholder = self.options.searchPlaceholder + } self.setLoading(false) self.endPinAnimation() - self.lastLocation = nil - self.navigationItem.searchController?.searchBar.placeholder = self.options.searchPlaceholder - return - } - self.lastLocation = location?.first - if let name = location?.first?.name { - self.navigationItem.searchController?.searchBar.placeholder = name - } else { - self.navigationItem.searchController?.searchBar.placeholder = self.options.searchPlaceholder + } - self.setLoading(false) + } else { self.endPinAnimation() - } - } else { - self.endPinAnimation() + if options.enableRangeSelection { + updateCircleOverlay() + updateRadiusHandlePosition() + } } } private func startPinAnimation() { @@ -362,6 +439,82 @@ class PlacePickerViewController: UIViewController { } }, completion: comp) } + + // MARK: - Radius selection helpers + private func setupRadiusSelection() { + updateCircleOverlay() + setupRadiusHandle() + updateRadiusHandlePosition() + } + + private func updateCircleOverlay() { + if let overlay = circleOverlay { + mapView.removeOverlay(overlay) + } + let circle = MKCircle(center: mapView.centerCoordinate, radius: currentRadius) + circleOverlay = circle + mapView.addOverlay(circle) + } + + private func setupRadiusHandle() { + if radiusHandle == nil { + let handle = UIView(frame: CGRect(x: 0, y: 0, width: 28, height: 28)) + handle.layer.cornerRadius = 14 + handle.layer.borderWidth = 2 + handle.layer.borderColor = UIColor(options.contrastColor).cgColor + handle.backgroundColor = UIColor(options.color) + let pan = UIPanGestureRecognizer(target: self, action: #selector(handleRadiusPan(_:))) + handle.addGestureRecognizer(pan) + self.view.addSubview(handle) + self.radiusHandle = handle + self.radiusPanGesture = pan + } + } + + @objc private func handleRadiusPan(_ gesture: UIPanGestureRecognizer) { + guard let handle = radiusHandle else { return } + let location = gesture.location(in: self.view) + let centerPoint = mapView.convert(mapView.centerCoordinate, toPointTo: self.view) + let dx = Double(location.x - centerPoint.x) + let dy = Double(location.y - centerPoint.y) + let distancePoints = sqrt(dx*dx + dy*dy) + let newRadiusMeters = pointsToMeters(points: distancePoints) + currentRadius = max(options.minRadius, min(options.maxRadius, newRadiusMeters)) + updateRadiusHandlePosition() + if gesture.state == .ended { + updateCircleOverlay() + } + if gesture.state == .ended { + _ = handle + } + } + + private func updateRadiusHandlePosition() { + guard let handle = radiusHandle else { return } + let center = mapView.centerCoordinate + let east = coordinateOffset(from: center, metersEast: currentRadius, metersNorth: 0) + let point = mapView.convert(east, toPointTo: self.view) + handle.center = point + } + + private func pointsToMeters(points: Double) -> Double { + let center = mapView.centerCoordinate + let east = coordinateOffset(from: center, metersEast: 1.0, metersNorth: 0) + let p1 = mapView.convert(center, toPointTo: self.view) + let p2 = mapView.convert(east, toPointTo: self.view) + let oneMeterPoints = hypot(Double(p2.x - p1.x), Double(p2.y - p1.y)) + if oneMeterPoints == 0 { return currentRadius } + return points / oneMeterPoints + } + + private func coordinateOffset(from coord: CLLocationCoordinate2D, metersEast: Double, metersNorth: Double) -> CLLocationCoordinate2D { + let earthRadius = 6378137.0 + let dLat = metersNorth / earthRadius + let dLon = metersEast / (earthRadius * cos(coord.latitude * .pi / 180)) + let lat = coord.latitude + (dLat * 180 / .pi) + let lon = coord.longitude + (dLon * 180 / .pi) + return CLLocationCoordinate2D(latitude: lat, longitude: lon) + } } extension PlacePickerViewController: MKMapViewDelegate { @@ -372,6 +525,18 @@ extension PlacePickerViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) { mapWillMove() } + func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { + if let circle = overlay as? MKCircle { + let renderer = MKCircleRenderer(circle: circle) + let fillColorHex = options.radiusColor.isEmpty ? options.color : options.radiusColor + let strokeColorHex = options.radiusStrokeColor.isEmpty ? options.color : options.radiusStrokeColor + renderer.fillColor = UIColor(fillColorHex).withAlphaComponent(0.2) + renderer.strokeColor = UIColor(strokeColorHex) + renderer.lineWidth = CGFloat(options.radiusStrokeWidth) + return renderer + } + return MKOverlayRenderer(overlay: overlay) + } } extension PlacePickerViewController: UISearchBarDelegate { diff --git a/ios/Types/PlacePickerOptions.swift b/ios/Types/PlacePickerOptions.swift index 53817cc..58e2d46 100644 --- a/ios/Types/PlacePickerOptions.swift +++ b/ios/Types/PlacePickerOptions.swift @@ -32,4 +32,18 @@ struct PlacePickerOptions: Record { var enableLargeTitle: Bool = true @Field var rejectOnCancel: Bool = true + @Field + var enableRangeSelection: Bool = false + @Field + var initialRadius: Double = 1000 + @Field + var minRadius: Double = 100 + @Field + var maxRadius: Double = 10000 + @Field + var radiusColor: String = "" + @Field + var radiusStrokeColor: String = "" + @Field + var radiusStrokeWidth: Double = 2 } diff --git a/ios/Types/PlacePickerResult.swift b/ios/Types/PlacePickerResult.swift index f9a0064..33b1724 100644 --- a/ios/Types/PlacePickerResult.swift +++ b/ios/Types/PlacePickerResult.swift @@ -14,4 +14,22 @@ struct PlacePickerResult: Record { var address: PlacePickerAddress? @Field var didCancel: Bool + @Field + var radius: Double? + @Field + var radiusCoordinates: RadiusCoordinates? +} + +struct BoundsCoordinates: Record { + @Field + var northeast: PlacePickerCoordinate + @Field + var southwest: PlacePickerCoordinate +} + +struct RadiusCoordinates: Record { + @Field + var center: PlacePickerCoordinate + @Field + var bounds: BoundsCoordinates } diff --git a/src/ReactNativePlacePicker.types.ts b/src/ReactNativePlacePicker.types.ts index 108a5a1..dc53798 100644 --- a/src/ReactNativePlacePicker.types.ts +++ b/src/ReactNativePlacePicker.types.ts @@ -92,6 +92,46 @@ export interface PlacePickerOptions { * @default true */ rejectOnCancel?: boolean; + /** + * @description Enable draggable radius selection overlay on the map. + * @type boolean + * @default false + */ + enableRangeSelection?: boolean; + /** + * @description Initial radius in meters when range selection is enabled. + * @type number + * @default 1000 + */ + initialRadius?: number; + /** + * @description Minimum allowed radius in meters when dragging the handle. + * @type number + * @default 100 + */ + minRadius?: number; + /** + * @description Maximum allowed radius in meters when dragging the handle. + * @type number + * @default 10000 + */ + maxRadius?: number; + /** + * @description Fill color of radius circle (hex or rgba). Uses `color` with alpha if not provided. + * @type string + */ + radiusColor?: string; + /** + * @description Stroke color of radius circle. + * @type string + */ + radiusStrokeColor?: string; + /** + * @description Stroke width of radius circle in pixels. + * @type number + * @default 2 + */ + radiusStrokeWidth?: number; } export interface PlacePickerResults { @@ -108,4 +148,18 @@ export interface PlacePickerResults { * @description Did cancel the place picker window without selecting. */ didCancel: boolean; + /** + * @description Selected radius in meters if `enableRangeSelection` is true. + */ + radius?: number; + /** + * @description Geometry of the selected radius area. + */ + radiusCoordinates?: { + center: PlacePickerCoordinate; + bounds: { + northeast: PlacePickerCoordinate; + southwest: PlacePickerCoordinate; + }; + }; }