// Copyright (C) Stichting Deltares 2018. All rights reserved.
//
// This file is part of the application DAM - Clients Library.
//
// DAM - UI is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using GeoAPI.Geometries;
using NetTopologySuite.Features;
using NetTopologySuite.Index.Quadtree;
namespace Deltares.Maps
{
///
/// Spatial Indexed Feature Repository
///
public class FeatureRepository : IFeatureRepository
{
private readonly IList emptyGeometryFeatures;
private readonly IList features;
private Quadtree spatialIndex;
public FeatureRepository()
{
features = new List();
emptyGeometryFeatures = new List();
spatialIndex = new Quadtree();
}
public static IFeatureRepository CreateFromShapeFile(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("fileName");
}
return CreateFromShapeFile(new ShapeFileLocation(fileName));
}
public IEnumerable Query(IFeature feature)
{
return Query(feature.Geometry);
}
public IEnumerable Query(IGeometry geometry)
{
return geometry.IsEmpty ? emptyGeometryFeatures : Query(geometry.EnvelopeInternal);
}
///
/// Gets features based on the envelope.
///
///
/// Note that this call won't handle empty geometries. To return empty geometries as well use Query(IFeature) or
/// Query(IGeometry)
/// instead of this call
///
///
///
public IEnumerable Query(Envelope envelope)
{
//return this.featureLookup.Values.Where(f => envelope.Intersects(f.Geometry.EnvelopeInternal));
return spatialIndex.Query((Envelope)envelope);
}
public static IFeatureRepository CreateFromShapeFile(ShapeFileLocation shapeFileLocation)
{
if (shapeFileLocation == null)
{
throw new ArgumentNullException("shapeFileLocation");
}
var repository = new FeatureRepository();
try
{
FeatureImporter.ImportFromShapeFile(shapeFileLocation, repository);
}
catch (Exception e)
{
throw e;
}
return repository;
}
public IEnumerable FeatureIds
{
get { return Features.Select(geom => geom.Id); }
}
public IGeometry GetGeometryById(Guid id)
{
//var feature = this.featureLookup.Values.FirstOrDefault(item => item.Id == id);
IFeature feature = Features.FirstOrDefault(item => item.Id == id);
if (feature != null)
{
return feature.Geometry;
}
throw new ArgumentException(string.Format("Unknown id '{0}', geometry not found", id));
}
public IEnumerable> GetAttributes(IGeometry geom)
{
if (geom == null)
{
throw new ArgumentNullException("geom");
}
IFeature feature =
Features.SingleOrDefault(
f => f.Geometry == geom || f.Geometry.AsText().GetHashCode() == geom.AsText().GetHashCode());
if (feature != null)
{
IAttributesTable attributes = feature.Attributes;
return attributes.GetNames().Select(attr => new KeyValuePair(attr, attributes[attr]));
;
}
throw new ArgumentException("Couldn't find the geometry in the repository");
}
public IEnumerable Features
{
get { return features.Concat(emptyGeometryFeatures); }
}
///
/// TODO: Is it allowed to have different attributes per geometry? If this is the case the we have to create a fast and
/// robust attribute collector.
///
public IEnumerable SupportedAttributes
{
get
{
if (Features == null || !Features.Any())
{
return new List();
}
return Features.SelectMany(geometry => geometry.Attributes.GetNames()).Distinct();
}
}
public void Add(IFeature feature)
{
if (feature == null)
{
throw new ArgumentNullException("feature");
}
if (feature.Geometry.IsEmpty)
{
emptyGeometryFeatures.Add(feature);
}
else
{
features.Add(feature);
AddToSpatialIndex(feature);
}
}
private void AddToSpatialIndex(IFeature feature)
{
try
{
spatialIndex.Insert(feature.Geometry.EnvelopeInternal, feature);
}
catch (Exception e)
{
throw e;
}
}
private void RemoveFromSpatialIndex(IFeature feature)
{
spatialIndex.Insert(feature.Geometry.EnvelopeInternal, feature);
}
public void Add(IEnumerable featureCollection)
{
if (featureCollection == null)
{
throw new ArgumentNullException("featureCollection");
}
foreach (IFeature feature in featureCollection)
{
Add(feature);
}
}
public void Remove(IFeature feature)
{
if (feature == null)
{
throw new ArgumentNullException("feature");
}
if (feature.Geometry.IsEmpty)
{
emptyGeometryFeatures.Remove(feature);
}
else
{
features.Remove(feature);
RemoveFromSpatialIndex(feature);
}
}
public void Remove(Func predicate)
{
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
var work = new Queue(Count);
foreach (IFeature feature in Features.Where(predicate))
{
work.Enqueue(feature);
}
while (work.Count > 0)
{
Remove(work.Dequeue());
}
}
public void Add(Feature feature, Func predicate)
{
if (feature == null)
{
throw new ArgumentNullException("feature");
}
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
if (predicate(feature))
{
Add(feature);
}
}
///
/// Clears the list of features in the repository
///
public void Clear()
{
features.Clear();
emptyGeometryFeatures.Clear();
spatialIndex = new Quadtree();
}
///
/// Gets the feature count in the repository
///
public int Count
{
get { return features.Count + emptyGeometryFeatures.Count; }
}
}
}