PolylineShape

PolylineShape에 대한 설명 및 사용 방법.

지도상에 특정 선형을 표시할 때 PolylineShape를 사용합니다. PolylineShape는 하나 이상의 Polyline으로 구성되며, Polyline은 두개 이상의 점으로 구성할 수 있습니다. PolylineShape는 Polyline을 구성하는 점들을 레벨에 따라 디테일한 정도를 내부적으로 조정하는 LOD(Level Of Detail)처리 없이 Polyline 형태 그대로 보여주기 때문에, 길찾기 라인을 표시하기에는 적합하지 않습니다. 길찾기 라인은 Route 페이지를 참고하시기 바랍니다.

PolylineStyleSet


PolylineStyleSet은 PolygonShape가 어떻게 표시될지를 정의합니다. PolygonStyleSet은 ShapeManager를 통해 생성할 수 있으며, 같은 styleID로는 overwrite 할 수 없습니다. PolylineStyleSet은 하나 이상의 PolylineStyle로 이루어져 있으며, PolylineStyle은 하나 이상의 레벨별 스타일(PerLevelPolylineStyle)로 구성됩니다. 그림으로 표시하면 styleSet의 구성은 아래와 같습니다.

PolylineShapeStyleSet

StyleSet에 여러개의 Style을 추가하면 추가한 순서대로 index가 부여되고, 여러개의 Polyline으로 이루어진 PolylineShape에 해당 styleSet을 적용하여 각 Polyline마다 사용할 styleIndex를 지정할 수 있습니다.

PolylineStyle

PolylineStyle을 구성하는 단위 레벨별 스타일의 구성요소는 아래와 같습니다.

Property Description
bodyColor Polyline의 body color
bodyWidth Polyline의 body 두께
strokeColor Polyline의 외곽선 색깔
strokeWidth Polyline의 외곽선 두께
level 해당 단위 스타일이 표출될 레벨

PolylineStyleSet 생성하기

PolylineStyleSet을 구성하는 프로퍼티는 아래와 같습니다.

Property Description
styleSetID PolylineStyleSet의 ID. 폴리라인은 이 ID를 키로 style을 사용할 수 있다
styles StyleSet을 구성하는 1개 이상의 PolylineStyle
capType Polyline의 cap type.
- PolylineCapTypeRound: Polyline 시작/끝 지점에 Round형태의 Cap
- PolylineCapTypeSquare: Polyline 시작/끝 지점에 Square형태의 Cap
- PolylineCapTypeButt: Polyline 시작/끝지점에 Butt형태의 Cap
- PolylineCapTypeArrow:Polyline 끝지점에 Arrow형태의 Cap. 해당 타입 사용시 Polyline의 stroke는 3이하로 권장
- PolylineCapTypeArrowRound: Polyline 끝지점에 ArrowRound형태의 Cap. 해당 타입 사용시 Polyline의 stroke는 3이하로 권장

PolylineShape

아래 예제는 위 스크린샷과 같은 형태의 1개의 Polyline을 갖는 PolylineShape를 표시하는 스타일을 생성하는 예제입니다.

    // StyleSet을 생성한다.
    // PolylineStyleSet은 한 개 이상의 PolylineStyle로 구성되고, PolylineStyle은 한 개 이상의 레벨별 스타일인 PerLevelPolylineStyle로 구성된다.
    // PerLevelPolylineStyle은 지정된 Polyline이 해당 레벨에서 어떻게 그려질지를 결정한다. 스타일이 지정되지 않았거나, 해당 레벨이 스타일이 지정되지 않은 구간이라면 Polyline은 그려지지 않는다.
    func createPolylineStyleSet() {
        let mapView = mapController?.getView("mapview") as! KakaoMap
        let manager = mapView.getShapeManager()
        let _ = manager.addShapeLayer(layerID: "PolylineLayer", zOrder: 10000)
        
        // 0 ~ 13레벨 : 빨간색 외곽선을 갖는 파란색 polyline
        // 14 ~ 16레벨 : 초록색 외곽선을 갖는 빨간색 polyline
        // 17 ~ 21레벨 : 빨간색 외곽선을 갖는 초록색 polyline
        let polylineStyle = PolylineStyle(styles: [
            PerLevelPolylineStyle(bodyColor: UIColor.blue, bodyWidth: 4, strokeColor: UIColor.red, strokeWidth: 1, level: 0),
            PerLevelPolylineStyle(bodyColor: UIColor.red, bodyWidth: 8, strokeColor: UIColor.green, strokeWidth: 2, level: 14),
            PerLevelPolylineStyle(bodyColor: UIColor.green, bodyWidth: 16, strokeColor: UIColor.red, strokeWidth: 3, level: 17)
        ])
        
        // 하나의 PolylineStyle을 포함하는 PolylineStyleSet을 만든다.
        let styleSet = PolylineStyleSet(styleSetID: "polylineStyleSet", styles: [polylineStyle])
        manager.addPolylineStyleSet(styleSet)
    }
    
    func createPolylineShape() {
        let mapView = mapController?.getView("mapview") as! KakaoMap
        let manager = mapView.getShapeManager()
        
        /// ShapeLayer추가
        let layer = manager.getShapeLayer(layerID: "PolylineLayer")
        let basePosition = MapPoint(longitude: 127.044395, latitude: 37.505754)
        let options = PolylineShapeOptions(shapeID: "polyline", styleID: "polylineStyleSet", zOrder: 1)
        
        // PolylineShape는 basePosition을 기준으로 하는 offset 좌표들로 구성할 수 있다.
        options.basePosition = basePosition
        options.polylines.append(Polyline(line: Primitives.getCirclePoints(radius: 100, cw: true), styleIndex: 0))

        let shape = layer?.addPolylineShape(options)
        shape?.show()
        
    }

PolylineShapeOptions


PolylineShape는 사용자가 직접 생성할 수 없습니다. ShapeManager를 통해 생성한 ShapeLayer에 아래와 같이 addPolylineShape를 호출하면서 생성 옵션을 넘기면, API 내부적으로 생성된 PolylineShape객체를 받아올 수 있습니다.

        // PolylineShape의 생성옵션을 지정하는 클래스.
        let options = PolylineShapeOptions(shapeID: "polyline", styleID: "polylineStyleSet", zOrder: 1)
        
        // PolylineShape는 basePosition을 기준으로 하는 offset 좌표들로 구성할 수 있다.
        options.basePosition = basePosition
        options.polylines.append(Polyline(line: Primitives.getCirclePoints(radius: 100, cw: true), styleIndex: 0))

        let shape = layer?.addPolylineShape(options)

PolylineShapeOptions의 속성은 아래와 같습니다. 각 Property를 지정하여 다양한 형태의 PolylineShape를 생성할 수 있습니다.

Property Description
shapeID PolylineShape 고유 ID. 같은 Layer안에서 중복 ID를 가질 수 없습니다. ID는 Layer 범위 내에서 유니크합니다.
styleID PolylineShape가 사용할 styleID.
PolylineShape를 생성하는 시점에서 style은 미리 생성되어 있어야 합니다.
zOrder PolylineShape의 렌더링 우선순위. Layer내에서 먼저 그려질지 결정하는 기준이 됩니다.
basePosition Polyline의 base가 되는 기준 좌표. 해당 좌표를 기준으로 Polyline의 버텍스가 그려집니다.
polylines PolylineShape를 구성할 하나 이상의 Polyline 배열.
Polyline은 두개 이상의 point로 구성할 수 있습니다.

Polyline

Polyline은 PolylineShape의 구성요소입니다. PolylineShape는 하나 이상의 Polyline으로 이루어져 있습니다. Polyline의 Property는 아래와 같습니다.

Property Description
line Polyline을 구성하는 2개 이상의 Point.
basePosition을 기준으로 실수형 offset으로 구성한다.
styleIndex Polyline이 StyleSet에서 사용할 스타일 인덱스
capType 지정된 라인의 끝을 둥글게 처리
- PolylineCapTypeNone: 시작과 끝 둘다 둥글게 처리하지 않습니다.
- PolylineCapTypeStart: 라인의 시작부분을 둥글게 처리합니다.
- PolylineCapTypeEnd: 라인의 끝부분을 둥글게 처리합니다.
- PolylineCapTypeBoth: 라인의 시작과 끝 부분을 둘다 둥글게 처리합니다.

여러개의 폴리라인을 사용하여 아래와 같은 형태의 PolylineShape도 구성할 수 있습니다.

PolylineShape

위 스크린샷은 5개의 polyline으로 구성된 하나의 PolylineShape입니다. 각 Polyline마다 다른 styleIndex를 사용하여 다르게 표시할 수 있습니다.

Polyline Properties


생성된 PolylineShape는 아래와 같은 종류의 Property를 가지고 있습니다. get only property를 제외하고는, property값을 바꾸면 새로운 값으로 반영됩니다.

Property Description
layerID PolylineShape가 속한 layerID
basePosition PolylineShape의 Base position
position shape의 Position. 값을 변경하면 업데이트됩니다.
orientation shape의 orientation. 값을 변경하면 업데이트됩니다.
isShow PolylineShape가 현재 뷰에 표시되고 있는지 여부

Style 및 Polyline 바꾸기


PolylineShape는 style과 PolylineShape를 구성하는 Polyline을 함께 바꾸는 인터페이스를 제공합니다.

changeStyleAndData(styleID:polylines:)

PolylineShape의 Style은 유지하고, 구성하는 Polyline만 업데이트하고자 하는 경우, 기존의 styleID를 넣고 업데이트 할 Polyline 배열을 넣어서 폴리라인만 업데이트 할 수 있습니다. 반대로, style만 업데이트하고자 하는 경우, 새로 추가한 styleID와 기존 Polyline data를 넣어서 스타일만 업데이트 할 수 있습니다.

이 인터페이스는 id가 그대로 유지되므로, PolylineShape가 가리키는 객체의 본질은 변하지 않지만, 데이터를 업데이트 할 필요가 있거나, 스타일을 바꾸어야 할 때 사용합니다.

아래 예제는 생성한 PolylineShape의 데이터를 업데이트 하는 예제입니다.

    //추가된 폴리라인에 점 하나를 랜덤하게 추가한다.
    // styleIndex는 추가한 styleSet의 5개의 스타일중 하나를 랜덤하게 사용한다.
    @objc func onUpdatePolyline() {
        let mapView: KakaoMap = mapController?.getView("mapview") as! KakaoMap
        let manager = mapView.getShapeManager()
        let layer = manager.getShapeLayer(layerID: "polylines")
        let line = layer?.getMapPolylineShape(shapeID: "line")
        
        // line을 구성하는 point 추가
        let pnt = (_points!.last?.kakaoCoord)!
        _points?.append(MapPoint(longitude: pnt.longitude + Double.random(in: 0...0.004491),
                                 latitude: pnt.latitude + Double.random(in: 0...0.004491)))
        
        // Polyline segment 생성. styleIndex는 5개중에 하나를 선택한다.
        let lineSeg = MapPolyline(line: _points!, styleIndex: UInt(arc4random_uniform(5)))
        
        // PolylineShape를 구성하는 lineSegment를 업데이트한다.
        line?.changeStyleAndData(styleID: "polylineStyleSet", lines: [lineSeg])
    }

Animator


PolylineShape는 Animator를 이용하여 애니메이션 효과를 적용할 수 있습니다. 특정 애니메이션 효과 옵션을 적용하여 ShapeManager를 통해 Animator를 생성할 수 있습니다. ShapeManager에서는 ShapeAnimator의 추가 및 삭제등의 관리를 하고 있습니다. 한번 생성한 Animator는 지우기 전까지 ShapeManager에서 관리하고 있으므로, ID를 이용해서 ShapeManager에서 가져올 수 있습니다.

생성한 Animator에는 PolylineShape를 추가할 수 있으며, Animator를 동작시키면 Animator에 속한 모든 PolylineShape에 일괄적으로 애니메이션 효과가 적용됩니다.

PolylineAnimator

ShapeAnimator는 ShapeManager를 통해서 생성할 수 있습니다. 이 때, Animator가 가질 ShapeAnimationEffect 종류인 애니메이션 효과를 지정할 수 있습니다. 현재까지 ShapeAnimationEffect 종류는 WaveAnimationEffect만 존재합니다. WaveAnimationEffect는 시작할 때 알파 & 스케일값과 끝날때의 알파 & 스케일 값을 지정하여 애니메이션 진행에 따라 Fade In/Out 효과와 점점 커지거나 작아지는 형태의 애니메이션 효과를 줄 수 있습니다. WaveAnimationEffect의 Property는 아래와 같습니다.

Property Description
startAlpha 애니메이션이 시작할때의 알파값
endAlpha 애니메이션이 끝날때의 알파값
startPixel 애니메이션이 시작할때의 radius(px)값. 이 픽셀값과 유사하게 폴리라인이 보이려면, Polyline의 길이 또는 반지름을 1.0정도로 설정하는것을 권장합니다.
endPixel 애니메이션이 끝날때의 radius(px)값. 이 픽셀값과 유사하게 폴리라인이 보이려면, Polyline의 길이 또는 반지름을 1.0정도로 설정하는것을 권장합니다.

또한 WaveAnimationEffect는 Style과 유사하게 레벨별로 효과를 다르게 설정할 수 있습니다.

아래 예제 코드는 WaveAnimationEffect를 적용한 ShapeAnimator 예제입니다.

    func createPolylineAnimator() {
        // absolute
        let mapView = mapController?.getView("mapview") as? KakaoMap
        let manager = mapView?.getShapeManager()
        
        /// ShapeLayer추가
        let layer = manager?.addShapeLayer(layerID: "shapes", zOrder: 10001)
        
        let colors = [UIColor.red, UIColor.orange, UIColor.yellow, UIColor.green, UIColor.blue, UIColor.purple]
        
        let styleSet = PolylineStyleSet(styleSetID: "lineStyle")
        
        for index in 0 ..< colors.count {
            
            /// PolylineStyle 추가
            let style = PolylineStyle(styles: [
                PerLevelPolylineStyle(bodyColor: colors[index], bodyWidth: 4, strokeColor: colors[index], strokeWidth: 0, level: 0),
                PerLevelPolylineStyle(bodyColor: colors[colors.count - index - 1], bodyWidth: 8, strokeColor: colors[colors.count - index - 1], strokeWidth: 0, level: 14)
            ])

            styleSet.addStyle(style)
        }

        manager?.addPolylineStyleSet(styleSet)
        
        let size: CGSize = (mapView?.viewRect.size)!
        let center = mapView?.getPosition(CGPoint(x: size.width / 2, y: size.height / 2))
        let option = MapPolylineShapeOptions(shapeID: "rings", styleID: "lineStyle", zOrder: 1)
        let weight = 0.2
        
        for circleIndex in 0 ..< 4 {
            let points = Primitives.getCirclePoints(radius: Float(1.0 - (weight * Double(circleIndex))), cw: true, center: center!)
            // circle point style 개수만큼 나눠서 생성생성
            for index in 0 ..< styleSet.styles.count {
                
                var startIndex = index * 15
                let endIndex = startIndex + 15
                
                var line = [MapPoint]()
                while startIndex <= endIndex {
                    line.append(points[startIndex])
                    startIndex += 1
                }
                
                let polyline = MapPolyline(line: line, capType: .none, styleIndex: UInt(index))
                option.polylines.append(polyline)
            }
        }

        let polyline = layer?.addMapPolylineShape(option)
        polyline?.show()
        _showShape = true
        
        /// shape Animator 추가
        let effect = WaveAnimationEffect()
        effect.addAnimationData(startAlpha: 0.8, endAlpha: 0.0, startPixel: 0, endPixel: 300, level: 0)
        effect.addAnimationData(startAlpha: 0.8, endAlpha: 0.0, startPixel: 50, endPixel: 400, level: 15)
        
        let animator = manager?.addShapeAnimator(animatorID: "circleWaveAnimator", interpolation: AnimationInterpolation(duration: 1000, repeat: 15, method: .cubinIn), effect: effect, hideAtStop: false)
        animator?.addMapPolylineShape(polyline!)

        animator?.start()
    }

ShapeAnimator는 아래와 같은 규칙을 가지고 동작합니다.

  • ShapeAnimator에 추가된 PolylineShape가 없을경우 Animator는 동작하지 않습니다.
  • ShapeAnimator를 start한 이후에는 Animator에 PolylineShape를 추가하거나 Animator에 있는 PolylineShape를 비우는등의 함수는 무시됩니다. 즉, PolylineShape를 추가하거나 Animator를 비우려면 반드시 stop을 호출한 이후에 수행해야 합니다.
  • Animator에 추가된 PolylineShape는 1회성 성격을 가지고 있습니다. 즉, Animator가 start 한 이후 stop을 호출하거나 Animation이 종료되면 Animator에 추가되어있던 모든 PolylineShape는 비워집니다. 따라서, 같은 애니메이션 효과를 반복해서 주고싶다면 Animation동작이 끝날때마다 PolylineShape를 다시 Animator에 추가해주어야 합니다.