Search code examples
ruby-on-railspostgresqlpostgis

Postgis : How to make only one query with 2 queries


Traces db :

A trace is an hiking path

  create_table "traces", force: :cascade do |t|
    t.string "name"
    t.geometry "path", limit: {:srid=>4326, :type=>"line_string"}
  end

Pois db :

A Poi is a Point of Interest (city, castel...)

create_table "pois", force: :cascade do |t|
    t.string "address"
    t.string "address2"
    t.integer "zip_code"
    t.string "city"
    t.string "department"
    t.string "region"
    t.float "latitude"
    t.float "longitude"
    t.geography "lonlat", limit: {:srid=>4326, :type=>"st_point", :geographic=>true}
  end

With the first query, I get an array of POIs(ptb => poi2) around one track (tr), from one POI(pta => poi1)

      SELECT
        ptb.* AS pois
        FROM traces tr, pois pta, pois ptb, locate_point_a
        WHERE tr.id = #{trace.id}
          AND pta.id = #{poi1.id}
          AND ST_DWithin(
          ST_LineSubstring(
          tr.path,
          ST_LineLocatePoint(tr.path, pta.lonlat::geometry) + (25 * 1000) / ST_Length(tr.path, false),
          ST_LineLocatePoint(tr.path, pta.lonlat::geometry) + (250 * 1000) / ST_Length(tr.path, false)
          )::geography,
          ptb.lonlat::geography,
          4000)

With the second query, I calculate the distance between one POI and an other POIs (on the track)

      SELECT
        ST_Distance(tr.path::geography, pta.lonlat::geography) +
        ST_Distance(tr.path::geography, ptb.lonlat::geography) +
        ST_Length(ST_LineSubstring(
          tr.path,
          least(ST_LineLocatePoint(tr.path, pta.lonlat::geometry), ST_LineLocatePoint(tr.path, ptb.lonlat::geometry)),
          greatest(ST_LineLocatePoint(tr.path, pta.lonlat::geometry), ST_LineLocatePoint(tr.path, ptb.lonlat::geometry))),false)  AS dst_line
        FROM traces tr, pois pta, pois ptb, locate_point_a, locate_point_b
        WHERE tr.id = #{trace.id}
          AND pta.id = #{poi1.id}
          AND ptb.id = #{poi2.id}

I would like to do only one query and get the liste of POIs around track (ordered by distance) and the distance from one POI to all the others POIs from the list (from the first query).

For exemple :

I'm starting in a town (pta). I would like to walk 25 kms (distance) and to know where I can find an hostel for sleep around this distance. With the first query, I can get a list, with all the hotels (ptb), 4000 m around the trace.

For exemple for the result of the first query, I get an unorderer list of poi.ids : [1, 7, 8, 3]

But, I need to know and display too, exactly how many kms there are between my start point(pta) and each hotel (ptb). Are they at 21 km, 22km or 24km... ?

So, with the result of the second query I get this info for each poi (from the first query) :
[1 => 21.6] [7 => 26.2] [8 => 21.2] [3 => 20.4 ]

The two queries do the job (but individually). I need to have the same results but with only one query.

An ordered list of all hotels with the mileage :

[3 => 20,4 , 8 => 21.2 ,  1=> 21,6 , 7 => 26,2]

Solution

  •   SELECT
        ST_Distance(tr.path::geography, pta.lonlat::geography) +
        ST_Distance(tr.path::geography, poi.lonlat::geography) +
        ST_Length(ST_LineSubstring(
          tr.path,
          least(ST_LineLocatePoint(tr.path, pta.lonlat::geometry), ST_LineLocatePoint(tr.path, poi.lonlat::geometry)),
          greatest(ST_LineLocatePoint(tr.path, pta.lonlat::geometry), ST_LineLocatePoint(tr.path, poi.lonlat::geometry))),false)  AS dst_line, poi.*
      FROM traces tr, pois pta, (
        SELECT poi.* AS pois
        FROM traces tr, pois pta, pois poi
        WHERE tr.id = #{trace.id}
        AND pta.id = #{poi.id}
        AND ST_DWithin(ST_LineSubstring(
                tr.path,
                ST_LineLocatePoint(tr.path, pta.lonlat::geometry) + (#{dist} * 1000) / (tr.length * 1000) ,
                1)::geography,
                poi.lonlat::geography,
                2000)
          ) as poi
        WHERE tr.id = #{trace.id}
        AND pta.id = #{poi.id}
        AND poi.id = poi.id
        ORDER BY dst_line ASC
    

    Now, I need to optimize it :D