본문 바로가기

프로젝트

Flutter Map에 장소 마커, 경로 표시

현재 프로젝트는 추천받은 장소에 대한 글 결과와 그 위에 단순히 지도만 표시되고 있다.

보다 직관적으로 해당 장소의 위치를 알려주기 위해 마커를 표시해보겠다.

 

UI 영역 FlutterMap 단의 TileLayer 밑 부분에 MarkerLayer를  넣는다. TileLayer 부분만 있다면 원래의 평범한 지도로 돌아온다.

 

사용한 코드도 올려보겠다.

// 지도 영역
          Container(
            height: 200,
            child: FlutterMap(
              options: MapOptions(
                initialCenter: LatLng(avgLat, avgLng), 
                initialZoom: 13
              ),
              children: [
                TileLayer(urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'),
                MarkerLayer(
                  markers: dayPlaces.map((place) {
                    double? lat = place['lat'] is double ? place['lat'] : double.tryParse(place['lat']?.toString() ?? '');
                    double? lng = place['lng'] is double ? place['lng'] : double.tryParse(place['lng']?.toString() ?? '');
                    
                    if (lat == null || lng == null) return Marker(point: LatLng(0, 0), child: Container());
                    
                    return Marker(
                      point: LatLng(lat, lng),
                      child: Icon(Icons.location_on, color: Colors.red),
                    );
                  }).toList(),
                ),
              ],
            ),
          ),

 

... 제대로 안된건지 마커가 제대로 표시되고 있는데 너무 작아서 안보이는건지 모르겠어서 이전 screen에서 반경 지름 km 와  사용자가 설정했던 줌레벨을 가져와서 해당 반경이 지도에 꽉차게 표시되게끔 만들어보겠다.

Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => TravelResultsScreen(
                        userConditions: {
                          ...widget.userConditions,
                          "location": {
                            "lat": currentCenter.latitude,
                            "lng": currentCenter.longitude,
                            "radius_km": actualRadiusKm,
                            "zoom_level" : currentZoom, // 현재 줌 레벨 추가
                          }
                        },
                      ),
                    ),

 

다음 화면으로 넘어갈때 필요한 정보를 전달해주면 된다. 반경 계산법은 이전 포스트에 함수와 함께 올려놨다.

@override
  Widget build(BuildContext context) {
    final scores = Map<String, dynamic>.from(widget.travelResult['scores'] ?? {});
    final places = widget.travelResult['places'] as List<dynamic>? ?? [];
    final zoom = widget.travelResult['location']?['zoom_level']?.toDouble() ?? 13.0;
    final availableDays = _getAvailableDays(places);
    final firstDayPlaces = places.where((p) => p['day'] == 1).toList();
    final LatLng initialCenter = firstDayPlaces.isNotEmpty
    ? LatLng(firstDayPlaces[0]['lat'], firstDayPlaces[0]['lng'])
    : LatLng(36.35, 127.38); // fallback center

필요한 screen 코드에 선언해주면 된다.

 

변수 막 복잡하게 들어가있는데 한번씩만 설명해보면

scores -> 우리 프로젝트에 쓰는 각 여행 태그별 점수 ex) 힐링 70점 이런식으로

places -> 코스 추천 결과로 생성된 장소들

zoom -> 사용자가 여행 반경을 설정할 때의 줌 레벨

availableDays -> Day마다 장소가 들어가야 해서 있는 변수

firstDayPlaces -> 첫번째 날에 있는 장소로 한정한다.

initialCenter

-> initialCenter: LatLng(36.3504, 127.3845) // 대전 중심

이런식으로 되어있으면 항상 대전 전역을 중심으로 지도가 보여지게 된다. 그러나 여행 코스마다 시작하는 장소가 다르므로 첫번째 장소의 좌표 중심으로 지도를 보여주는 것이다. 첫번째 장소 좌표를 못 가져오면 대전 전역으로 fallback 된다.

 

// 지도 영역
          Container(
            margin: EdgeInsets.symmetric(vertical: 8),
            height: 200,
            child: FlutterMap(
              options: MapOptions(
                initialCenter: LatLng(36.3504, 127.3845),
                initialZoom: 13,
              ),
              children: [
                TileLayer(
                  urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                ),
                MarkerLayer(
                  markers: _buildMarkersForSelectedDay(places, selectedDay),
                ),
              ],
            ),
          ),

initial Center가 장소들의 첫 좌표, initialZoom에 가져온 zoom레벨을 넣어준다.

 

마커는 잘 해결됐다. 다음은 이 장소들을 좀 더 직관적으로 최적 경로선으로 이어주고, 에러나고 있는 자동차랑 도보 소요 시간 부분도 가져와보겠다.

 

 

아래는 디버그 하면서 발생했던 문제들을 모아놨다.


'lat': fields['location']?['mapValue']?['fields']?['lat']?['doubleValue'] ?? 0.0,
        'lng': fields['location']?['mapValue']?['fields']?['lng']?['doubleValue'] ?? 0.0,

이 부분이었을 것이다. type이 다르게 들어갔을 수 있는 문제로 double로 바꿔보고 오겠다.

'lat': double.tryParse(
         fields['location']?['mapValue']?['fields']?['lat']?['doubleValue']?.toString() ?? ''
       ) ?? 0.0,
'lng': double.tryParse(
         fields['location']?['mapValue']?['fields']?['lng']?['doubleValue']?.toString() ?? ''
       ) ?? 0.0,