Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature proposal in Cartesian Factory : locate_point and interpolate_point #99

Open
ldonnet opened this issue May 29, 2015 · 0 comments
Labels

Comments

@ldonnet
Copy link

ldonnet commented May 29, 2015

I need to implement 2 methods in my development in Cartesian Factory :

  • locate_point : give a point in argument and return a percentage of linestring length
  • interpolate_point : give a percentage in argument and return a point on the linestring

I don't know if it's interesting to add this feature in the core project but if you need I could add it with spec.

module RGeo

  module Cartesian

    module LineStringMethods

      def locate_point(target)
        distance_on_line(target) / length
      end

      def distance_on_line(target)
        nearest_locator = nearest_locator(target)
        nearest_locator.distance_on_segment + distance_from_departure_to_segment(nearest_locator.segment)
      end

      def distance_from_departure_to_segment(segment)
        index = _segments.index(segment) 
        _segments[0...index].inject(0.0){ |sum_, seg_| sum_ + seg_.length }
      end

      def nearest_locator(target)
        locators(target).min_by(&:distance_from_segment)
      end

      def locators(point)
        _segments.collect { |segment| segment.locator(point) }
      end

      def interpolate_point(location)
        return points.last if location >= 1
        return points.first if location <= 0

        distance_on_line_string = location * length

        line_distance_at_departure = line_distance_at_arrival = 0
        segment = _segments.find do |segment|
          line_distance_at_arrival += segment.length
          line_distance_at_departure = line_distance_at_arrival - segment.length
          line_distance_at_arrival > distance_on_line_string
        end

        return nil if segment.blank?

        location_on_segment = (distance_on_line_string - line_distance_at_departure) / segment.length
        dx_location, dy_location = segment.dx * location_on_segment, segment.dy * location_on_segment
        factory.point(segment.s.x + dx_location, segment.s.y + dy_location)
      end

    end

    class Segment

      attr_reader :lensq

      def locator(target)
        PointLocator.new target, self
      end

    end

    class PointLocator
      include Math

      attr_reader :target, :segment

      def initialize(target, segment)
        @target = target
        @segment = segment
        raise "Target is not defined" unless target
      end

      def distance_on_segment
        segment.tproj(target) * segment.length
      end

      def distance_from_segment
        return 0 if segment.contains_point?(target)          
        ::Math.sqrt( target_distance_from_departure ** 2 - distance_on_segment ** 2 )
      end

      def target_distance_from_departure        
        segment.s.distance target
      end

    end

  end

end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants