Wednesday, October 16, 2013

Dynamically creating hexagons from real-world data (Part 3 - Data-Services)


In this iteration of my experiment I've add a services layer that is able to fetch real world-info in GeoJSON format, instead of having it hard-coded as on the previous post.

Also, I've added a small degree of interaction on the hexagon board creation, as the user is now able to choose where the board will be created. Nothing too fancy though.

As a finishing touch I've also improved the overall appearance of the hexagons. The following image shows the end-result.




The idea

Purely from an end-result point-of-view this post doesn't seem to be very ambitious over the previous ones. The base change is that instead of having an hexagon board immediately created on load it allows the user to specify where it should be created.


Steps

The truth is that there's lots of plumbing behind the scenes. Long-story short, these are the steps that lead up to this:
  • Created a SQL 2012 Database to store the spatial data

  • Developed a small application that is able to load/parse data from Shapefiles and store it in the database.
  • Created a REST Service that is able to return the spatial data in GeoJSON format



  • The data-access is done using Entity Framework 6 and I'm using NetTopologySuite to generate the GeoJson.


Currently the main action looks like this:
public ContentResult Features(
    string featureType, 
    double north, 
    double south, 
    double east, 
    double west, 
    double tolerance)
{
    var wktReader = new WKTReader();
    var boundingBox = GeoUtil.GetBoundingBox(north, west, south, east);
    using (var context = new RealHexagonsContext())
    {
    
        var geometries =
            new Collection<Feature>(
                context.Features
                .Where(f => f.Type == featureType)
                .Where(f => SqlSpatialFunctions
                                 .MakeValid(f.Geo)
                                 .Intersects(boundingBox))
                .Select(f => new { 
                            f.Name, 
                            f.Type, f.SubType, 
                            Geo = SqlSpatialFunctions
                                  .MakeValid(SqlSpatialFunctions
                                  .Reduce(f.Geo, tolerance)) })
                .ToList()
                .Select(feature =>
                {
                    var attributes = new AttributesTable();
                    attributes.AddAttribute("name", feature.Name);
                    attributes.AddAttribute("type", feature.Type);
                    attributes.AddAttribute("subtype", feature.SubType);
                    return new Feature(
                        wktReader.Read(feature.Geo.AsText()), 
                        attributes);
                })
                .ToList());

        var featureCollection = new FeatureCollection(geometries);

        var writer = new GeoJsonWriter();

        string result = writer.Write(featureCollection);

        return Content(result, "application/json");
    }
}

It returns all the features of a certain type inside a bounding-box (defined by the corners of the viewport) in GeoJson.
  • Modified the web-client to support this service
I used jQuery to make some ajax calls and underscore to iterate the collections. The remaining logic is mostly similar to what I did on my previous post, although some bugs were fixed.
  • Added a small degree of interaction to the website.
Created an overlay on the map with a rectangle and two buttons, one to create the hexagon area and one to remove it.


  • Improved the cosmetic of the hexagons a little bit.
Most of the area hexagons are now using some textures, as seen in this picture. Nothing spectacular though :).



In Motion

As I haven't deployed this yet, here's a short video showing the end-result.


Ending remarks

Everything is still being done on client-side, using my own Canvas TileLayer Module. As I mentioned on the video, I'll eventually push some of these calculations to server-side and cache/store the results to speed-up the whole map.

Also, I'm not really expecting to create a part 4 of this series. I do plan to use this approach for an interesting project I've got on my backlog. Stay tuned ;)



No comments:

Post a Comment