I am writing code in a C # library to allow clustering in a (two-dimensional) data set - essentially splitting the data into groups or clusters. To be useful, a library must accept "shared" or "user" data, put it in and return cluster data.
To do this, I need to assume that each base point in the transmitted dataset has a two-dimensional vector associated with it (in my case Lat , Lng - I work with coordinates).
My first thought was to use common types and pass in two lists one list of common data (i.e. List<T> ) and another of the same length defining 2D vectors (i.e. List<Coordinate> , where Coordinate is my class for specifying lat (lng) pairs, where the lists match each other by index. But this is rather tedious, because it means that in the algorithm I have to somehow track these indexes.
My next thought was to use inferfaces where I define the interface
public interface IPoint { double Lat { get; set; } double Lng { get; set; } }
and make sure that the data I transmit implements this interface (i.e. I can assume that each element passed has Lat and a Lng ).
But for me, this does not work either. I am using my C # library to stop clusters in a transit network (in another project). The class is called Stop , and this class is also from an external library, so I cannot implement an interface for this class.
What I did then was inherited from Stop by creating a class called ClusterableStop , which looks like this:
public class ClusterableStop : GTFS.Entities.Stop, IPoint { public ClusterableStop(Stop stop) { Id = stop.Id; Code = stop.Code; Name = stop.Name; Description = stop.Description; Latitude = stop.Latitude; Longitude = stop.Longitude; Zone = stop.Zone; Url = stop.Url; LocationType = stop.LocationType; ParentStation = stop.ParentStation; Timezone = stop.Timezone; WheelchairBoarding = stop.WheelchairBoarding; } public double Lat { get { return this.Latitude; } } public double Lng { get { return this.Longitude; } } }
which, as you can see, implements the IPoint interface. Now I use the constructor for ClusterableStop to first convert all Stop in the dataset to ClusterableStop s, then run the algorithm and get the result as ClusterableStop s.
This is not exactly what I want, because I want to do something in Stop based on which cluster they fall into. I canโt do this because I actually created an instance of new stops, namely ClusterableStop !!
I can still achieve what I want, because, for example, I can restore the original objects by identifier. But of course, is there a much more elegant way to accomplish all this? Is it right to use interfaces? It seemed like such a simple idea - transitioning and returning user data - but it turned out to be so complicated.