Draw inverted circle and calculate zoom level based on radius in Google Maps
Trong bài viết này, chúng ta sẽ tìm hiểu về cách dùng các class có sẵn trong Google Maps SDK để làm những thứ không có sẵn. Cụ thể: Vẽ một hình tròn với bán kính cho trước có phần bên trong tranperancy và phần map bên ngoài hình tròn được fill bởi màu khác. Tính zoom level phù hợp dựa trên bán ...
Trong bài viết này, chúng ta sẽ tìm hiểu về cách dùng các class có sẵn trong Google Maps SDK để làm những thứ không có sẵn. Cụ thể:
- Vẽ một hình tròn với bán kính cho trước có phần bên trong tranperancy và phần map bên ngoài hình tròn được fill bởi màu khác.
- Tính zoom level phù hợp dựa trên bán kính của một đường tròn cho trước.
Trong Google Maps để vẽ một hình tròn fill màu bên trong rất đơn giản. Chúng ta chỉ cần class có sẵn GMSCircle và vài dòng code. Ví dụ để vẽ một hình tròn có bán kính 500m tại vị trí hiện tại của camera ta có:
fileprivate func drawCircle(centerOn coordinate: CLLocationCoordinate2D) { let circle = GMSCircle(position: coordinate, radius: 500) circle.fillColor = UIColor.red.withAlphaComponent(0.2) circle.strokeColor = .red circle.strokeWidth = 2.0 circle.map = mapView }
Kết quả:
Tuy nhiên để vẽ một hình tròn với phần bên trong trong suốt, phần map bên ngoài được fill màu thì không đơn giản như vậy. Để làm điều này, ý tưởng là fill màu lên toàn bộ bề mặt trái đất rồi khoét một hình tròn màu trong suốt lên đó. Đọc kĩ trang document của Google Maps SDK phần Shapes, ta thấy có một class tên là GMSPolygon có vẻ khả thi. Vì:
- Class GMSPolygon giúp vẽ một đa giác trên map bằng việc nối các điểm tọa độ với nhau.
- Class GMSPolygon có một property là holes thuộc kiểu GMSMutablePath giúp vẽ một đường khép kín bên trong.
Vậy thì chúng ta cần:
Bước 1. Thể hiện một hình tròn có bán kính cho trước bằng class GMSMutablePath
fileprivate func createHoles(in coordinate: CLLocationCoordinate2D, radius: CLLocationDistance) -> GMSMutablePath { let circlePath = GMSMutablePath() // 1. let centerPoint = mapView.projection.point(for: coordinate) // 2. let radius = mapView.projection.points(forMeters: radius, at: coordinate) var circlePoint = CGPoint() for angle in 0..<360 { // 3. circlePoint.x = centerPoint.x + radius * CGFloat(cos(Double(angle) * .pi / 180.0)) circlePoint.y = centerPoint.y + radius * CGFloat(sin(Double(angle) * .pi / 180.0)) // 4. circlePath.add(mapView.projection.coordinate(for: circlePoint)) } return circlePath }
-
Sử dụng class GMSProjection chuyển toạ độ tâm đường tròn trên thực tế (latitude, longitude) sang toạ độ CGPoint trên view.
-
Tính toán độ dài bán kính đường tròn theo đơn vị CGFloat.
-
Sử dụng các hàm lượng giác cơ bản để tính toán 360 điểm nằm trên đường tròn tương ứng với góc hợp thành từ 0° -> 360°.
-
Thêm các điểm nằm tính được để cấu tạo nên đường tròn bằng GMSMutablePath.
Bước 2. Vẽ một đa giác lên toàn bộ bề mặt trái đất.
Vẽ một đa giác fill màu bao phủ lên toàn bộ bề mặt trái đất thì ta có thể vẽ qua các điểm cực với (latitude, longitude) tương ứng: A (-85.05115, -180.0) -> B (85.05115, -180.0) -> C (85.05115, 0.0) -> D (85.05115, 180.0) E (-85.0515, 180.0) -> F (-85.0515, 0.0) -> A (-85.05115, -180.0)
Tuy nhiên do kinh độ -180° và 180° trùng nhau lên khi vẽ qua các điểm như trên ta sẽ thu được kết quả không được như ý muốn:
Vậy thì thay vì vẽ một đa giác thì thử vẽ 2 đa giác: 1 cái phủ lên bán cầu tây, 1 cái bán cầu đông xem sao.
Khai báo 2 property GMSPolygon tương ứng.
let westernHemisphere = GMSPolygon() let easternHemisphere = GMSPolygon()
Hàm vẽ đa giác.
fileprivate func drawPolygons() { // Draw Western Hemisphere let westernPath = GMSMutablePath() westernPath.addLatitude(-85.05115, longitude: -180.0) westernPath.addLatitude(85.05115, longitude: -180.0) westernPath.addLatitude(85.05115, longitude: 0.0) westernPath.addLatitude(-85.05115, longitude: 0.0) westernHemisphere.fillColor = UIColor.blue.withAlphaComponent(0.2) westernHemisphere.path = westernPath westernHemisphere.map = mapView // Draw Eastern Hemisphere let easternPath = GMSMutablePath() easternPath.addLatitude(-85.05115, longitude: 0.0) easternPath.addLatitude(85.05115, longitude: 0.0) easternPath.addLatitude(85.05115, longitude: 180.0) easternPath.addLatitude(-85.05115, longitude: 180.0) easternHemisphere.fillColor = UIColor.blue.withAlphaComponent(0.2) easternHemisphere.path = easternPath easternHemisphere.map = mapView }
Hàm vẽ hình tròn cuối cùng.
fileprivate func drawInvertedCircle(centerOn coordinate: CLLocationCoordinate2D) { drawPolygons() let hole = createHoles(in: coordinate, radius: 500) // Nếu tâm hình tròn nằm ở bán cầu tây thì set holes cho bán cầu tây và ngược lại. if coordinate.longitude < 0.0 { westernHemisphere.holes = [hole] } else { easternHemisphere.holes = [hole] } }
Sửa lại fillColor trong hàm vẽ hình tròn lúc đầu thành .clearColor, gọi hàm drawInvertedCircle(