1

I want these lines to be visible on a regular map in such a way where each square represents 1x1m.enter image description here

I looked into MKTileOverlay but didn't find too much about it. Is it possible to show the gridline on the map as well as change the color?

grantespo
  • 2,233
  • 2
  • 24
  • 62

1 Answers1

2

I've done something very similar for an app I've been playing around with. Mine is for putting a coloured grid over a map so that there are 15 columns and rows in a square mile around a home location, so you'll need to adjust the calculations for your distances but the same general approach should work. The app is only a prototype at the moment, and hasnt been optimised (could refactor code out of viewDidLoad for a start!), but the code should be good enough to get you started.

var homeLocation: CLLocationCoordinate2D!
let metresPerMile = 1609.344
var degPerHorizEdge: Double!
var degPerVertEdge: Double!

override func viewDidLoad() {
   homeLocation = CLLocationCoordinate2D(latitude: 53.7011, longitude: -2.1071)
   let hd = CLLocation(latitude: homeLocation.latitude, longitude: homeLocation.longitude).distance(from: CLLocation(latitude: homeLocation.latitude + 1, longitude: homeLocation.longitude))
   let vd = CLLocation(latitude: homeLocation.latitude, longitude: homeLocation.longitude).distance(from: CLLocation(latitude: homeLocation.latitude, longitude: homeLocation.longitude + 1))
   let degPerHMile = 1 / (hd / metresPerMile)
   let degPerVMile = 1 / (vd / metresPerMile)
   degPerHorizEdge = degPerHMile / 15
   degPerVertEdge = degPerVMile / 15
   super.viewDidLoad()
   let gridController = GridController(for: gameID!)
   gridController.delegate = self
   let mapSize = CLLocationDistance(1.2 * metresPerMile)
   let region = MKCoordinateRegion(center: homeLocation, latitudinalMeters: mapSize, longitudinalMeters: mapSize)
   mapView.delegate = self
   mapView.showsUserLocation = true
   mapView.showsBuildings = true
   mapView.mapType = .standard
   mapView.setRegion(region, animated: true)
}          

override func viewDidAppear(_ animated: Bool) {
   super.viewDidAppear(animated)
   if let overlays = prepareOverlays() {
      mapView.addOverlays(overlays)
   }
}

func prepareOverlays() -> [MKPolygon]? {
   let topLeft = CLLocationCoordinate2D(latitude: homeLocation.latitude - 7.5 * degPerHorizEdge, longitude: homeLocation.longitude - degPerVertEdge * 7.5)
   var overlays = [MKPolygon]()
   var locations = [CLLocationCoordinate2D]()
      for y  in 0...14 {
         for x in 0...14 {
            locations.append(CLLocationCoordinate2D(latitude: topLeft.latitude + Double(x) * degPerHorizEdge, longitude: topLeft.longitude + Double(y) * degPerVertEdge))
         }
      }
   for coord in locations.enumerated() {
      let location = coord.element
      var corners = [location,    //has to be a var due to using pointer in next line
                CLLocationCoordinate2D(latitude: location.latitude + degPerHorizEdge, longitude: location.longitude),
                CLLocationCoordinate2D(latitude: location.latitude + degPerHorizEdge, longitude: location.longitude + degPerVertEdge),
                CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude + degPerVertEdge)]
      let overlay = MKPolygon(coordinates: &corners, count: 4)
      overlay.title = "\(coord.offset)"
      overlays.append(overlay)
    }
    return overlays.count > 0 ? overlays : ni
 }

    //MARK:- MKMapViewDelegate

 extension MapViewController: MKMapViewDelegate {  
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
       // overlay is a WSW zone
       if let polygon = overlay as? MKPolygon {
          let renderer = MKPolygonRenderer(polygon: polygon)
          renderer.strokeColor = UIColor.gray.withAlphaComponent(0.4)
          renderer.fillColor = UIColor.orange.withAlphaComponent(0.5)
          renderer.lineWidth = 2
          return renderer
       }
          // overlay is a line segment from the run (only remaining overlay type)
       else {
          let renderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
          renderer.strokeColor = UIColor.blue.withAlphaComponent(0.8)
          renderer.lineWidth = 3
          return renderer
       }
      }
    }
flanker
  • 3,840
  • 1
  • 12
  • 20
  • I've left the last else clause in to show that you still need to process any other overlays if there are any. If you have none it's not relevant. – flanker Oct 31 '19 at 15:14
  • Your answer really helped! But as I want to build a grid much bigger(covering the whole earth, or at least the whole visible map area), memory is a big issue. My simulator/phone freezes trying to load the whole grid. Any ideas how to solve this problem? – ProudHKer May 10 '21 at 07:08
  • You onluy need to manage the display of the grid over the visible area, and with a 1m by 1m grid surely you don't want to display it at low zoom levels as it would be not be visible? – flanker May 11 '21 at 23:59
  • Oh yes, thank you! Not sure how to implement it here though. And the grid would still need to be visible at low zoom levels in my case, as the grid will change in scale as the zoom level changes. – ProudHKer May 12 '21 at 05:22
  • If you are scaling the grid to the zoom level I can't see why it should be causing a freeze. I suspect the issue is elsewhere and not the directly due to the overlay. – flanker May 12 '21 at 13:13
  • At the time of writing, I didn't implement the scaling yet, but only a large grid, so it freezes. Now I have implemented it, and it doesn't freeze, but there are still many problems. Above `let location = coord.element`, I inserted a condition to check whether the `coord` is within `visibleMapRect`; if it is, it is appended to `overlays`. However, it takes a second to load the grid on the new `visibleMapRect` whenever I zoom or pan. From an app I saw elsewhere, there is no loading time. Users can pan and zoom continuously all over the world with a grid. I don't know how it's achieved. – ProudHKer May 17 '21 at 07:38