/// <summary> /// Serialises a Rectangle into a TomTom skipper record /// </summary> /// <param name="Rectangle">The rectangle to serialise</param> /// <returns>The serialised rectangle</returns> private byte[] SerialiseRectangle(GeoRect Rectangle) { byte[] bReturn = new byte[SkipperStructSize]; Int32 AllPOIsSize = SkipperStructSize; //Calculate Size of POIs foreach(POI CurrentPOI in Rectangle.POIs) { AllPOIsSize += GetPOISize(CurrentPOI); } //Serialise bReturn[0] = 0x01; ConvertToLEBytes(AllPOIsSize).CopyTo(bReturn, 1); ConvertToLEBytes((Int32)(Rectangle.TopRight.TomTomXPos)).CopyTo(bReturn, 5); ConvertToLEBytes((Int32)(Rectangle.TopRight.TomTomYPos)).CopyTo(bReturn, 9); ConvertToLEBytes((Int32)(Rectangle.BottomLeft.TomTomXPos)).CopyTo(bReturn, 13); ConvertToLEBytes((Int32)(Rectangle.BottomLeft.TomTomYPos)).CopyTo(bReturn, 17); return bReturn; }
/// <summary> /// Creates the OV2 file header /// </summary> /// <param name="BoundingRect">The full bounding rectangle for all POIs within the OV2</param> /// <param name="Rectangles">List containing all rectangles and POIs contained in the OV2</param> /// <param name="AllPOIs">A list of all POIs within all the calculated rectangles</param> /// <returns>The byte array file header</returns> private byte[] GenerateOV2Header(GeoRect BoundingRect, List<GeoRect> Rectangles, List<POI> AllPOIs) { byte[] bReturn = new byte[SkipperStructSize]; Int32 Size = SkipperStructSize; //Calculate total size for every POI in the OV2 foreach (POI CurrentPOI in AllPOIs) { Size += GetPOISize(CurrentPOI); } //Calculate total size of all skipper records in teh database Size += (Rectangles.Count * SkipperStructSize); //Serialise data bReturn[0] = 0x01; ConvertToLEBytes(Size).CopyTo(bReturn, 1); ConvertToLEBytes((Int32)(BoundingRect.TopRight.TomTomXPos)).CopyTo(bReturn, 5); ConvertToLEBytes((Int32)(BoundingRect.TopRight.TomTomYPos)).CopyTo(bReturn, 9); ConvertToLEBytes((Int32)(BoundingRect.BottomLeft.TomTomXPos)).CopyTo(bReturn, 13); ConvertToLEBytes((Int32)(BoundingRect.BottomLeft.TomTomYPos)).CopyTo(bReturn, 17); return bReturn; }
/// <summary> /// Calculates the full dounding rectangle around a list of POIs /// </summary> /// <param name="POIs">The list of POIs</param> /// <returns>The bounding rectangle</returns> private GeoRect CalculateBoundingRect(List<POI> POIs) { GeoRect ReturnRect = new GeoRect(); //Initialise rect with max/min values ReturnRect.BottomLeft.x = 90; //West co-ordinates are negative, so set to max value ReturnRect.BottomLeft.y = 180; //South co-ordinates are negative, so set to max value ReturnRect.TopRight.x = -90; //East co-ordinates are positive, so set to min value ReturnRect.TopRight.y = -180; //North co-ordinates are positive, so set to min value //For all POIs foreach(POI CurrentPOI in POIs) { //Calculate bounding rectangle for all POIs if (CurrentPOI.Long < ReturnRect.BottomLeft.x) { ReturnRect.BottomLeft.x = CurrentPOI.Long; } if (CurrentPOI.Long > ReturnRect.TopRight.x) { ReturnRect.TopRight.x = CurrentPOI.Long; } if (CurrentPOI.Lat < ReturnRect.BottomLeft.y) { ReturnRect.BottomLeft.y = CurrentPOI.Lat; } if (CurrentPOI.Lat > ReturnRect.TopRight.y) { ReturnRect.TopRight.y = CurrentPOI.Lat; } } //All POIs are in this rectangle ReturnRect.POIs = POIs; return ReturnRect; }
/// <summary> /// Splits a rectangle along its longest side /// </summary> /// <param name="Rectangle">The ectangle to split</param> /// <returns>A list containing the split rectangles</returns> private List<GeoRect> SplitRectangle(GeoRect Rectangle) { List<GeoRect> lReturn = new List<GeoRect>(); double NorthSouthDistance = GeoDifference(Rectangle.BottomLeft.y, Rectangle.TopRight.y); double WestEastDifference = GeoDifference(Rectangle.BottomLeft.x, Rectangle.TopRight.x); //Is the North to South line the longest? if (NorthSouthDistance >= WestEastDifference) { //Split rectangle along North-South GeoRect TopRect = new GeoRect( new GeoPoint(Rectangle.BottomLeft.x, Rectangle.BottomLeft.y + (NorthSouthDistance / 2)), new GeoPoint(Rectangle.TopRight.x, Rectangle.TopRight.y) ); GeoRect BottomRect = new GeoRect( new GeoPoint(Rectangle.BottomLeft.x, Rectangle.BottomLeft.y), new GeoPoint(Rectangle.TopRight.x, Rectangle.TopRight.y - (NorthSouthDistance / 2)) ); //Add new rectangles to array lReturn.Add(TopRect); lReturn.Add(BottomRect); } //The West to East line is longest else { //Split rectangle along West-East GeoRect LeftRect = new GeoRect( new GeoPoint(Rectangle.BottomLeft.x, Rectangle.BottomLeft.y), new GeoPoint(Rectangle.TopRight.x - (WestEastDifference / 2), Rectangle.TopRight.y) ); GeoRect RightRect = new GeoRect( new GeoPoint(Rectangle.BottomLeft.x + (WestEastDifference / 2), Rectangle.BottomLeft.y), new GeoPoint(Rectangle.TopRight.x, Rectangle.TopRight.y) ); //Add new rectangles to array lReturn.Add(LeftRect); lReturn.Add(RightRect); } return lReturn; }
/// <summary> /// Determins if the rectangle is equal to or smaller than the minimum size of a POI bounding rectangle /// </summary> /// <param name="Rectangle">The Rectangle</param> /// <returns>True if the rectangle is equal to or smaller than the minimum size for a POI bounding rectangle</returns> private bool RectangleIsMinSize(GeoRect Rectangle) { bool bReturn = false; if( GeoDifference(Rectangle.BottomLeft.x, Rectangle.TopRight.x) <= minRectSize.Width && GeoDifference(Rectangle.BottomLeft.y, Rectangle.TopRight.y) <= minRectSize.Height) { bReturn = true; } return bReturn; }
/// <summary> /// Creates a list of rectangles with a maximum of 'MaxPOIsInRectangle' POIs in each rectangle /// </summary> /// <param name="Rectangles">The rectangles to process</param> /// <returns>A list containing the processed rectangles</returns> private List<GeoRect> ProcessPOIs(GeoRect BoundingRectangle) { List<GeoRect> lReturn = new List<GeoRect>(); bool bSplitRect = true; int i = 0; GeoRect CurrentRect = null; List<GeoRect> RectsToProcess = new List<GeoRect>(); List<GeoRect> ProcessedRects = null; //Initialise rectangles to process with full bounding rectangle RectsToProcess.Add(BoundingRectangle); //Keep looping until we do not split anything! - Replaced recurrsive call with loop due to stack overflows with large areas! while(bSplitRect) { bSplitRect = false; ProcessedRects = new List<GeoRect>(); for(i = 0; i < RectsToProcess.Count; i++) { CurrentRect = RectsToProcess[i]; if(CurrentRect.POIs.Count > maxPOIsInRectangle && !RectangleIsMinSize(CurrentRect)) { //Split the rectangle into 2 List<GeoRect> SplitRectangles = SplitRectangle(CurrentRect); //Determine which POIs reside in which of the split rectangles DeterminePOIsInSplitRectangles(SplitRectangles, CurrentRect.POIs); //Add our 2 new rectangles for further processing ProcessedRects.AddRange(SplitRectangles); //We have split the rectangle! bSplitRect = true; } else if (CurrentRect.POIs.Count != 0) { lReturn.Add(CurrentRect); } } RectsToProcess = new List<GeoRect>(ProcessedRects); } return lReturn; }