Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Parsing Latitude and Longitude Information

, 21 Feb 2012
Parses user input and extracts latitude and longitude information, taking into account the user's language and regional settings
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Geospatial
{
    /// <summary>
    /// Represents a collection of Locations and allows their serialization and
    /// deserialization from XML.
    /// </summary>
    public sealed class LocationCollection : ICollection<Location>, IXmlSerializable
    {
        private readonly List<Location> locations = new List<Location>();

        /// <summary>
        /// Gets the number of Locations contained in this instance.
        /// </summary>
        public int Count
        {
            get { return this.locations.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is read-only.
        /// </summary>
        bool ICollection<Location>.IsReadOnly
        {
            get { return false; }
        }

        /// <summary>Adds a Location to this instance.</summary>
        /// <param name="item">The Location to be added.</param>
        /// <exception cref="ArgumentNullException">item is null.</exception>
        public void Add(Location item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            this.locations.Add(item);
        }

        /// <summary>Removes all Locations from this instance.</summary>
        public void Clear()
        {
            this.locations.Clear();
        }

        /// <summary>
        /// Determines whether a specified Location is contained in this instance.
        /// </summary>
        /// <param name="item">The Location to locate.</param>
        /// <returns>
        /// true if item is found in this instance; otherwise, false. This
        /// method also returns false if the specified value parameter is null.
        /// </returns>
        public bool Contains(Location item)
        {
            if (item == null)
            {
                return false;
            }
            return this.locations.Contains(item);
        }

        /// <summary>
        /// Copies this instance to a compatible one-dimensional array, starting
        /// at the specified index of the target array.
        /// </summary>
        /// <param name="array">The destination one-dimensional array.</param>
        /// <param name="arrayIndex">
        /// The zero-based index in array at which copying begins.
        /// </param>
        /// <exception cref="ArgumentException">
        /// The number of Locations contained in this instance is greater than the
        /// available space from arrayIndex to the end of the destination array.
        /// </exception>
        /// <exception cref="ArgumentNullException">array is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// arrayIndex is less than 0.
        /// </exception>
        public void CopyTo(Location[] array, int arrayIndex)
        {
            this.locations.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Returns an enumerator that iterates through this instance.
        /// </summary>
        /// <returns>An enumerator for this instance.</returns>
        public IEnumerator<Location> GetEnumerator()
        {
            return this.locations.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through this instance.
        /// </summary>
        /// <returns>An enumerator for this instance.</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// Removes the first occurrence of a specific Location from this instance.
        /// </summary>
        /// <param name="item">The Location to remove.</param>
        /// <returns>
        /// true if the specified value parameter is successfully removed;
        /// otherwise, false. This method also returns false if the specified
        /// value parameter was not found or is null.
        /// </returns>
        public bool Remove(Location item)
        {
            if (item == null)
            {
                return false;
            }
            return this.locations.Remove(item);
        }

        /// <summary>This method is reserved and should not be used.</summary>
        /// <returns>This method always returns null.</returns>
        /// <remarks>
        /// The IXmlSerializable interface documentation specifies that this
        /// method should always return null.
        /// </remarks>
        System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>Generates an object from its XML representation.</summary>
        /// <param name="reader">
        /// The XmlReader stream from which the object is deserialized.
        /// </param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // Important we check if it's an empty element because if it's not
            // we need to call ReadEndElement, which will skip the next element
            // if this element is empty, meaning data will be skipped.
            if (reader.IsEmptyElement)
            {
                reader.Skip();
            }
            else
            {
                foreach (var value in SplitString(reader.ReadString(), '/'))
                {
                    Location location;
                    if (Location.TryParse(value, LocationStyles.Iso, CultureInfo.InvariantCulture, out location))
                    {
                        this.locations.Add(location);
                    }
                }
                reader.ReadEndElement();
            }
        }

        /// <summary>Converts an object into its XML representation.</summary>
        /// <param name="writer">
        /// The XmlWriter stream to which the object is serialized.
        /// </param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            var sb = new StringBuilder(16 * this.locations.Count); // 16 is the minimum that the ISO string will be
            foreach (var location in this.locations)
            {
                sb.Append(location.ToString("ISO", CultureInfo.InvariantCulture));
            }
            writer.WriteString(sb.ToString());
        }

        // String.Split eats the separator and also uses lots of memory on long strings
        private static IEnumerable<string> SplitString(string input, char separator)
        {
            if (string.IsNullOrEmpty(input))
            {
                yield break;
            }

            int start = 0;
            int index = input.IndexOf(separator);
            while (index != -1)
            {
                // First increase the index so we include the separator in the
                // returned string and also to start searching from the position
                // after the previous find.
                index++;
                int length = index - start;
                yield return input.Substring(start, length);

                start = index;
                index = input.IndexOf(separator, index);
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Samuel Cragg

United Kingdom United Kingdom
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 21 Feb 2012
Article Copyright 2011 by Samuel Cragg
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid