Use List<List<GeoLocation>> inside a Filter

Mattias Högnäs
Member since: 2002
 

I have a property that is of type List<List<GeoLocation>> on the pagetype Im doing my search on.
I also have a variable that each is of type GeoLocation.

What I would like to do is something like query.Filter(x => [GeoLocationVariable].WithinAnyOf(x.[ListProperty]) and add that as a filter.

I foudn the Within() method on List<GeoLocation> but how would one go about doing the enumeration over the outer List in [ListProperty]?

#66128 Feb 20, 2013 18:45
  •  

    If possible given what you are building I would recommend flattening the list, that is, adding a property that converts the List<List<GeoLocation>> to an IEnumerable<GeoLocation>. By doing so you'll have data in your index which is easier to query.

    #66131 Feb 20, 2013 22:50
  • Mattias Högnäs
    Member since: 2002
     

    Hi Joel,

    I don't think flattening the list would help me in what I am trying to acomplish, but I could be wrong.
    Let me explain:
    I am trying to find out if the variable (GeoLocation) fits into (Within()) any of the areas (ie one area is a List<GeoLocation>).
    In other words each item in the outer list is describing an area made up of GeoLocation:s for each point.
    So if I make the list flat List<GeoLocation> and use Within() on it it wouldn't be the same as asking Within() on each item as a List<GeoLocation> in the outer list, or would it?

    #66132 Edited, Feb 20, 2013 23:13
  •  

    Aha, I see. Interesting problem. Find has a lot of functionality for geo search but pretty much all of it works in the other direction - ie you have single coordinates indexed and you filter by such a coordinate being within a certain range of a coordinate that you supply at query time, or within a bounding box made up of several coordinates which you supply at query time (that's what the Within method is for). What you want to do though is the opposite. I can think of several possible solutions but none of them is obvious.

    In order for me and others to help you, would it be possible for you to describe the use case in more detail? Keep in mind that some solutions may involve drastically changing what is indexed so as much context as you can possibly give would be great.

    #66139 Feb 21, 2013 9:28
  • Mattias Högnäs
    Member since: 2002
     

    Heh, yeah it might not be the most common of use cases. :)

    Ok, here goes:
    So I have this scenario of a site that has alot of sub pages, each sub page representing a company, each company has subpages representing an area. Each area represent a geographical area that the company is active inside.
    The areas are crated using a custom built property vitalizing Google Maps so you can plot an area and save it.

    So basicly the page for each company has a property like so:

    public IEnumerable<IEnumerable<GeoLocation>> Areas
            {
                get
                {
                    return this.GetChildrenByPageType(typeof(ShopLocationPage).Name, true)
                                    .OfType<ShopLocationPage>()
                                    .Where(childPage => childPage.Area != null))
                                    .Select(childPage => childPage.Area));
                }
            }

    The indexing of all pages is done using the default indexer but I have a relation added to conventions so the Areas propery will be updated when one area is updated like so:

    ContentIndexer.Instance.Conventions
                              .ForInstancesOf<ShopPage>()
                              .RelatedContent(model =>
                              {
                                  var relatedContent = new List<IContent>();
                                  if (!(model.ContentLink.CompareToIgnoreWorkID(ContentReference.StartPage) ||
                                        model.ContentLink.CompareToIgnoreWorkID(ContentReference.RootPage)))
                                  {
                                      relatedContent.Add(DataFactory.Instance.Get<IContent>(model.ParentLink));
                                  }
                                  return relatedContent;
                              });

    So when a visitor does a search for a company it has an option to write a location (Like a city or such) wich is then translated using Google Maps to a GeoLocation. This GeoLocation will be the variable in the Filter I am trying to create.

    #66144 Edited, Feb 21, 2013 10:25
  •  

    Thanks Mattias. 

    The REST API has functionality that probably can be used for this but the .NET API doesn't have that feature yet unless we can come up with some "creative" solution. Wheels are turning :)

    #66151 Feb 21, 2013 13:18
  •  

    Hi,

    One approach I would do in this case is to index the exact location of the shop and then in the query set the ordering to the distance from the location from where the user "searching". To not get results to far away maybe also use a filter with some sort of relevant max distance.

    Regards,
    Henrik


    #66153 Feb 21, 2013 14:51
  • Mattias Högnäs
    Member since: 2002
     

    Hi Henrik,

    That wouldn't really work since a company can be active in multiple areas, not multiple adresses.
    The areas are not adresses in that meaning of the word that they have one single adress, think of them as areas that are represented and each made up of a polygon of geolocations. And the areas themselves are not necceray in the form of circle around any location so using "WithinDistanceFrom" wouldn't work.

    #66154 Edited, Feb 21, 2013 14:54
  • Mattias Högnäs
    Member since: 2002
     

    Joel,

    I have been trying to find anyhting like this in the documentation but without any luck.

    Every aspect of the geographical search seem focused on one indexed location vs many/one variable not the other way round.

    This is sort of what I was hoping for:

    query = query.Filter(x => variableAsGeoLocation.WithinAny(x.Points))

    Or:

    query = query.Filter(x => x.Points.Any(variableAsGeoLocation.Within))

       

     

    #66159 Edited, Feb 21, 2013 15:35
  •  

    The locations could be indexed separatly pointing to the same shop but unfortunately you would still rely on the circular shape.


    #66161 Feb 21, 2013 15:39
  •  

    Mattias, 

    Correct, currently all supported methods revolves having a single (or multiple unrelated) coordinates in the index and filtering based on those. I *think* (that's really Henrik) it would be possible to add support for filtering based on indexed geographical shapes/areas but that's on a level where both the REST API and .NET API would need to be updated - in other words, a feature EPiServer has to deliver.

    Is there some other possible solution for your problem? Ie, can we enforce some constraints in terms of number of coordinates that describe the shape or  something like that?

    #66167 Feb 21, 2013 16:03
  • Mattias Högnäs
    Member since: 2002
     

    Joel,

    Ok, well as far as constraints concern I could possibly enforce the companies to one area each so that Areas returns IEnumerable<GeoLocation>, do you think that would make any difference?

    #66170 Edited, Feb 21, 2013 16:37
  •  

    I'm afraid I don't think so. A workable (though impractical) constraint could be if they described each area as a number of coordinates where each coordinate had a fixed radius. In other words, describing each area as one or more circles. Other than that I can't really think of anything that's currently supported other than doing more of the querying on the web server. 

    #66172 Feb 21, 2013 17:00
  • Mattias Högnäs
    Member since: 2002
     

    In the documentation I found this "multipolygon" type (It's in the bottom of the page):
    http://www.elasticsearch.org/guide/reference/mapping/geo-shape-type.html

    So if any developer on Find in here is reading this: Please add support for the multipolygon type but even more so, please also add support for a "reversed" Within, that is Filer(x => variable.Within(x.Property).
    I acctually think that is a more common use-case than Filter(x => x.Property.Within(variable)) if you think about it.
    Lets for example take mobile positioning for a mobile web, you'd have the variable as a single location and probably the indexed postions not only as sinngle locations but also as polygons from map services.

    #66205 Edited, Feb 22, 2013 23:52