select index to keep from candidate list by external function

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
0
down vote

favorite












Given a shape (a closed LineString, i.e. sequence of Points, where the first and the last points are equal) that contains other duplicates than the first and last point.



The algorithm finds all duplicated points and for each deletes all of them but one(or two). The tricky part is to decide which to keep: if either is equal to the first or last, keep it. Else keep the one, that has the least areal error.



object PolygonValidator 
class LineString(val lineString: Seq[LngLat])
def closeLine: Seq[LngLat] = lineString match
case head :: tail if head != tail.last ⇒ lineString :+ head
case closedLine ⇒ closedLine


/**
* calculates a number representing the area surrounded of the given lineString.<br/>
* assumes a flat interpretation, so use only for smaller geometries.
*
* @return <ul><li>value > zero when lineString is oriented clockwise<li>value < zero when lineString is oriented counter clockwise<li>zero when area is empty</ul>
*/
def areaRepresentation: Double = pairwise(lineString).foldLeft(0d)((sum, line) ⇒ line match
case (a, b) ⇒ sum + (b.lng - a.lng) * (b.lat + a.lat)
)


implicit def seqToLineString(s: Seq[LngLat]): LineString = new LineString(s)

def findDuplicate[C](shape: Seq[C]): Seq[C] = shape.tail.diff(shape.tail.distinct).distinct.reverse

private def pairwise[C](linear: Seq[C]): Seq[(C, C)] = linear.zip(linear.tail)

def removeDoublePoints(shape: Seq[LngLat]): Seq[LngLat] =
var pointsToDelete: Set[Int] = Set()
for (duplicate ← findDuplicate(shape))
val positionsOfDuplicate: Seq[Int] = shape.zipWithIndex.filter(_._1 == duplicate).map(_._2)
// […]

// keep original point with the smallest error
// TODO: make nicer :)
var start = if (positionsOfDuplicate.head == 0) 1 else 0
var end = if (positionsOfDuplicate.last == shape.size - 1) positionsOfDuplicate.size - 1 else positionsOfDuplicate.size
if (start == 0 && end == positionsOfDuplicate.size)
// we need to keep either
val startIdx = positionsOfDuplicate.head
val endIdx = positionsOfDuplicate.last
val startError = shape.slice(startIdx - 1, startIdx + 2).closeLine.areaRepresentation.abs
val endError = shape.slice(endIdx - 1, endIdx + 2).closeLine.areaRepresentation.abs
if (startError > endError) start += 1
else end -= 1

pointsToDelete = pointsToDelete ++ positionsOfDuplicate.slice(start, end)

shape.zipWithIndex.filterNot( case (_, idx) ⇒ pointsToDelete.contains(idx) ).map(_._1)


}


The part I am not so happy with is the big block in the removeDoublePoints function that only determines whether we should keep the first or the last of the given duplicate points.



Who has a nicer i.e. more readable or shorter version?







share|improve this question



























    up vote
    0
    down vote

    favorite












    Given a shape (a closed LineString, i.e. sequence of Points, where the first and the last points are equal) that contains other duplicates than the first and last point.



    The algorithm finds all duplicated points and for each deletes all of them but one(or two). The tricky part is to decide which to keep: if either is equal to the first or last, keep it. Else keep the one, that has the least areal error.



    object PolygonValidator 
    class LineString(val lineString: Seq[LngLat])
    def closeLine: Seq[LngLat] = lineString match
    case head :: tail if head != tail.last ⇒ lineString :+ head
    case closedLine ⇒ closedLine


    /**
    * calculates a number representing the area surrounded of the given lineString.<br/>
    * assumes a flat interpretation, so use only for smaller geometries.
    *
    * @return <ul><li>value > zero when lineString is oriented clockwise<li>value < zero when lineString is oriented counter clockwise<li>zero when area is empty</ul>
    */
    def areaRepresentation: Double = pairwise(lineString).foldLeft(0d)((sum, line) ⇒ line match
    case (a, b) ⇒ sum + (b.lng - a.lng) * (b.lat + a.lat)
    )


    implicit def seqToLineString(s: Seq[LngLat]): LineString = new LineString(s)

    def findDuplicate[C](shape: Seq[C]): Seq[C] = shape.tail.diff(shape.tail.distinct).distinct.reverse

    private def pairwise[C](linear: Seq[C]): Seq[(C, C)] = linear.zip(linear.tail)

    def removeDoublePoints(shape: Seq[LngLat]): Seq[LngLat] =
    var pointsToDelete: Set[Int] = Set()
    for (duplicate ← findDuplicate(shape))
    val positionsOfDuplicate: Seq[Int] = shape.zipWithIndex.filter(_._1 == duplicate).map(_._2)
    // […]

    // keep original point with the smallest error
    // TODO: make nicer :)
    var start = if (positionsOfDuplicate.head == 0) 1 else 0
    var end = if (positionsOfDuplicate.last == shape.size - 1) positionsOfDuplicate.size - 1 else positionsOfDuplicate.size
    if (start == 0 && end == positionsOfDuplicate.size)
    // we need to keep either
    val startIdx = positionsOfDuplicate.head
    val endIdx = positionsOfDuplicate.last
    val startError = shape.slice(startIdx - 1, startIdx + 2).closeLine.areaRepresentation.abs
    val endError = shape.slice(endIdx - 1, endIdx + 2).closeLine.areaRepresentation.abs
    if (startError > endError) start += 1
    else end -= 1

    pointsToDelete = pointsToDelete ++ positionsOfDuplicate.slice(start, end)

    shape.zipWithIndex.filterNot( case (_, idx) ⇒ pointsToDelete.contains(idx) ).map(_._1)


    }


    The part I am not so happy with is the big block in the removeDoublePoints function that only determines whether we should keep the first or the last of the given duplicate points.



    Who has a nicer i.e. more readable or shorter version?







    share|improve this question























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      Given a shape (a closed LineString, i.e. sequence of Points, where the first and the last points are equal) that contains other duplicates than the first and last point.



      The algorithm finds all duplicated points and for each deletes all of them but one(or two). The tricky part is to decide which to keep: if either is equal to the first or last, keep it. Else keep the one, that has the least areal error.



      object PolygonValidator 
      class LineString(val lineString: Seq[LngLat])
      def closeLine: Seq[LngLat] = lineString match
      case head :: tail if head != tail.last ⇒ lineString :+ head
      case closedLine ⇒ closedLine


      /**
      * calculates a number representing the area surrounded of the given lineString.<br/>
      * assumes a flat interpretation, so use only for smaller geometries.
      *
      * @return <ul><li>value > zero when lineString is oriented clockwise<li>value < zero when lineString is oriented counter clockwise<li>zero when area is empty</ul>
      */
      def areaRepresentation: Double = pairwise(lineString).foldLeft(0d)((sum, line) ⇒ line match
      case (a, b) ⇒ sum + (b.lng - a.lng) * (b.lat + a.lat)
      )


      implicit def seqToLineString(s: Seq[LngLat]): LineString = new LineString(s)

      def findDuplicate[C](shape: Seq[C]): Seq[C] = shape.tail.diff(shape.tail.distinct).distinct.reverse

      private def pairwise[C](linear: Seq[C]): Seq[(C, C)] = linear.zip(linear.tail)

      def removeDoublePoints(shape: Seq[LngLat]): Seq[LngLat] =
      var pointsToDelete: Set[Int] = Set()
      for (duplicate ← findDuplicate(shape))
      val positionsOfDuplicate: Seq[Int] = shape.zipWithIndex.filter(_._1 == duplicate).map(_._2)
      // […]

      // keep original point with the smallest error
      // TODO: make nicer :)
      var start = if (positionsOfDuplicate.head == 0) 1 else 0
      var end = if (positionsOfDuplicate.last == shape.size - 1) positionsOfDuplicate.size - 1 else positionsOfDuplicate.size
      if (start == 0 && end == positionsOfDuplicate.size)
      // we need to keep either
      val startIdx = positionsOfDuplicate.head
      val endIdx = positionsOfDuplicate.last
      val startError = shape.slice(startIdx - 1, startIdx + 2).closeLine.areaRepresentation.abs
      val endError = shape.slice(endIdx - 1, endIdx + 2).closeLine.areaRepresentation.abs
      if (startError > endError) start += 1
      else end -= 1

      pointsToDelete = pointsToDelete ++ positionsOfDuplicate.slice(start, end)

      shape.zipWithIndex.filterNot( case (_, idx) ⇒ pointsToDelete.contains(idx) ).map(_._1)


      }


      The part I am not so happy with is the big block in the removeDoublePoints function that only determines whether we should keep the first or the last of the given duplicate points.



      Who has a nicer i.e. more readable or shorter version?







      share|improve this question













      Given a shape (a closed LineString, i.e. sequence of Points, where the first and the last points are equal) that contains other duplicates than the first and last point.



      The algorithm finds all duplicated points and for each deletes all of them but one(or two). The tricky part is to decide which to keep: if either is equal to the first or last, keep it. Else keep the one, that has the least areal error.



      object PolygonValidator 
      class LineString(val lineString: Seq[LngLat])
      def closeLine: Seq[LngLat] = lineString match
      case head :: tail if head != tail.last ⇒ lineString :+ head
      case closedLine ⇒ closedLine


      /**
      * calculates a number representing the area surrounded of the given lineString.<br/>
      * assumes a flat interpretation, so use only for smaller geometries.
      *
      * @return <ul><li>value > zero when lineString is oriented clockwise<li>value < zero when lineString is oriented counter clockwise<li>zero when area is empty</ul>
      */
      def areaRepresentation: Double = pairwise(lineString).foldLeft(0d)((sum, line) ⇒ line match
      case (a, b) ⇒ sum + (b.lng - a.lng) * (b.lat + a.lat)
      )


      implicit def seqToLineString(s: Seq[LngLat]): LineString = new LineString(s)

      def findDuplicate[C](shape: Seq[C]): Seq[C] = shape.tail.diff(shape.tail.distinct).distinct.reverse

      private def pairwise[C](linear: Seq[C]): Seq[(C, C)] = linear.zip(linear.tail)

      def removeDoublePoints(shape: Seq[LngLat]): Seq[LngLat] =
      var pointsToDelete: Set[Int] = Set()
      for (duplicate ← findDuplicate(shape))
      val positionsOfDuplicate: Seq[Int] = shape.zipWithIndex.filter(_._1 == duplicate).map(_._2)
      // […]

      // keep original point with the smallest error
      // TODO: make nicer :)
      var start = if (positionsOfDuplicate.head == 0) 1 else 0
      var end = if (positionsOfDuplicate.last == shape.size - 1) positionsOfDuplicate.size - 1 else positionsOfDuplicate.size
      if (start == 0 && end == positionsOfDuplicate.size)
      // we need to keep either
      val startIdx = positionsOfDuplicate.head
      val endIdx = positionsOfDuplicate.last
      val startError = shape.slice(startIdx - 1, startIdx + 2).closeLine.areaRepresentation.abs
      val endError = shape.slice(endIdx - 1, endIdx + 2).closeLine.areaRepresentation.abs
      if (startError > endError) start += 1
      else end -= 1

      pointsToDelete = pointsToDelete ++ positionsOfDuplicate.slice(start, end)

      shape.zipWithIndex.filterNot( case (_, idx) ⇒ pointsToDelete.contains(idx) ).map(_._1)


      }


      The part I am not so happy with is the big block in the removeDoublePoints function that only determines whether we should keep the first or the last of the given duplicate points.



      Who has a nicer i.e. more readable or shorter version?









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jan 29 at 13:40









      Julien Rousé

      446416




      446416









      asked Jan 29 at 13:33









      Jan

      17616




      17616

























          active

          oldest

          votes











          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186256%2fselect-index-to-keep-from-candidate-list-by-external-function%23new-answer', 'question_page');

          );

          Post as a guest



































          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes










           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186256%2fselect-index-to-keep-from-candidate-list-by-external-function%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Python Lists

          Aion

          JavaScript Array Iteration Methods