Learn Courses My Dashboard

How to get results that are WITHIN/NEAR a MK region?

I’m working in a real estate app like Zillow…I show a View with a list of properties and a map with annotations for each property.

I have an array of properties(each with lat/lon) and I calculate the MKCoordinateRegion after a MKLocalSearch. All good, but I want to filter to get ONLY the properties near the MKCoordinateRegion (so I can show them in a list)

how can I do that?

function to reconcile the region

func reconcileLocation(location: MKLocalSearchCompletion) {
        let searchRequest = MKLocalSearch.Request(completion: location)
        let search = MKLocalSearch(request: searchRequest)
        search.start { (response, error) in
            if error == nil, let coordinate = response?.mapItems.first?.placemark.coordinate {
                self.coordinate = coordinate
                self.region = MKCoordinateRegion(center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.03, longitudeDelta: 0.03))
                self.isLoading = false
            }
        }
    }

function to filter listings

func getListingsFiltered(location: String, PriceMin: String, PriceMax: String, Bedrooms: Int ) {
        
        var listingsF = [Listing]() //new array instance of listing
        
        var lowestPrice = 0
        var highestPrice = 1000000000
        
        if PriceMin == "Any" {lowestPrice = 0} else {lowestPrice = Int(PriceMin) ?? 0}
        if PriceMax == "Any" {highestPrice = 1000000000} else { highestPrice = Int(PriceMax) ?? 1000000000}
        
        //append the ones that match
        for listing in self.results!.listings {
            //condition price
            if Int(listing.price)! >= lowestPrice && Int(listing.price)! <= highestPrice {
                //condition bedrooms
                if Int(listing.bedrooms!)! >= Bedrooms {
                    //condition location
                    
                    var l = Listing()
                    l.name = listing.name
                    l.neighborhood = listing.neighborhood
                    l.url = listing.url
                    l.price = listing.price
                    l.city = listing.city
                    l.state = listing.state
                    l.zipcode = listing.zipcode
//etc

                    listingsF.append(l)
                }
                

            }
            
        }
        
        DispatchQueue.main.async {
            self.listingsF = listingsF
            self.filters = true
            
        }
        
    }

I tried to compare “selected region” vs “listing region” (basically is MKcoordinateregion == MKcoordinateregion) but I got an error.

If you convert your MKCoordinateRegion to an MKMapRect, you can use its contains(_:) and intersects(_:) methods to determine if an MKMapPoint (which you can get from a CLCoordinate2D) is within it or intersects with it.

Or you can convert your MKCoordinateRegion to a CLCircularRegion and then use its contains(_:) method to check if your CLCoordinate2D is inside.

1 Like

roosterboy saving me as always, I implemented this function , in case somebody else is looking for the same functionality, thanks!!!

    func checkRegion(location: CLLocationCoordinate2D, contains childLocation: CLLocationCoordinate2D, with radius: Double)-> Bool {
        let region = CLCircularRegion(center: location, radius: radius, identifier: "SearchId")
            return region.contains(childLocation)
    }

calling the function

let listingRegion = CLLocationCoordinate2D(latitude: listing.lat!, longitude: listing.lon!)
                    
self.checkRegion(location: regionSearch.center, contains: listingRegion, with: 1000)

If you don’t mind a little bikeshedding on your API…

extension MKCoordinateRegion {
    func contains(_ location: CLCoordinate2D, within radius: Double, id: String = "SearchId") -> Bool {
        let container = CLCircularRegion(center: self.center, radius: radius, identifier: id)
        return container.contains(location)
    }
}

//Listing already contains stored properties for latitude and longitude,
//so we can construct a CLCoordinate2D from them when requested
extension Listing {
    var coordinate: CLCoordinate2D? {
        guard let latitude = self.lat, 
              let longitude = self.lon else {
            return nil
        }
        
        return CLCoordinate2D(latitude: latitude, longitude: longitude)
    }
}

//usage:
if let coordinate = listing.coordinate, 
   regionSearch.contains(coordinate, within: 1000) {
    //do stuff here
}

IMO, this reads much cleaner and is easier to understand. The main points being:

  1. Since you are trying to determine if a location lies within a given region, you should ask the region rather than relying on a separate function.
  2. checkRegion is very ambiguous. Check it for what? That name really tells us nothing about what the function is doing.
  3. The with parameter suffers from the same kind of ambiguity. With what? within is much clearer what we’re asking about. A parameter name that tells you what the number represents (meters, in this case) would be even better, but is harder to name well.
1 Like