public TopoTrailInfo(ITopoTrailUpdateRequest data, List <GpxTrackData> tracks)
        {
            Name        = data.Name;
            Description = data.Description;

            Keywords = data.Keywords;
            UrlText  = data.UrlText;
            UrlLink  = data.UrlLink;

            Timezone = GeoTimezoneInfo.Find(data.Timezone);
            if (Timezone == null)
            {
                Timezone = GeoTimezoneInfo.UTC;
            }

            Country  = GeoCountryInfo.Find(data.Country);
            Region   = GeoRegionInfo.Find(data.Region);
            Location = data.Location;

            if (tracks != null)
            {
                foreach (var track in tracks)
                {
                    var t = new TopoTrackInfo(this, track);
                    _tracks.Add(t);
                }
            }
        }
        /// <summary>
        /// Validates the request to create a new trail
        /// </summary>
        public ValidationServiceResponse <TopoTrailInfo> ValidateCreate(ITopoTrailUpdateRequest request)
        {
            // load existing trail
            var response = new ValidationServiceResponse <TopoTrailInfo>(null);

            // do basic validation
            if (String.IsNullOrWhiteSpace(request.Name))
            {
                response.AddError("Name", "Name is required!");
            }

            // TODO: REFACTOR: use GeoPolitical
            var timezone = GeoTimezoneInfo.Find(request.Timezone);

            if (timezone == null)
            {
                response.AddError("Timezone", "Timezone is missing or invalid!");
            }

            var country = GeoCountryInfo.Find(request.Country);

            if (country == null)
            {
                response.AddError("Country", "Country is missing or invalid!");
            }

            var region = GeoRegionInfo.Find(request.Region);

            if (region == null && !String.IsNullOrWhiteSpace(request.Region))
            {
                response.AddError("Region", "Region is invalid!");
            }

            return(response);
        }
        public TopoTrailInfo(GpxFileData data)
        {
            Name        = data.Name;
            Description = data.Description;

            Keywords = data.Keywords;
            UrlText  = data.UrlText;
            UrlLink  = data.UrlLink;

            Timezone = GeoTimezoneInfo.Find(data.Extensions.Timezone);
            if (Timezone == null)
            {
                Timezone = GeoTimezoneInfo.UTC;
            }

            Country  = GeoCountryInfo.Find(data.Extensions.Country);
            Region   = GeoRegionInfo.Find(data.Extensions.Region);
            Location = data.Extensions.Location;

            foreach (var track in data.Tracks)
            {
                var t = new TopoTrackInfo(this, track);
                _tracks.Add(t);
            }
        }
        /// <summary>
        /// Displays all of a country's regions and their bounds on a map
        /// </summary>
        public ActionResult Country(string id)
        {
            var model = InitModel();

            model.SelectedCountry = GeoCountryInfo.Find(id);

            return(View(model));
        }
        public IHttpActionResult FindTimezoneByLocation(string name)
        {
            var timezone = Graffiti.Geo.GuessTimezone(GeoRegionInfo.Find(name));

            if (timezone != null)
            {
                return(GetTimezone(timezone?.TZID ?? ""));
            }

            timezone = Graffiti.Geo.GuessTimezone(GeoCountryInfo.Find(name));
            return(GetTimezone(timezone?.TZID ?? ""));
        }
        /// <summary>
        /// Shows a map and list of all places in a given country
        /// </summary>
        public ActionResult Country(string id, string placeType = "")
        {
            var model = InitModel();

            model.SelectedCountry = GeoCountryInfo.Find(id);
            model.Places          = _cartoPlaceService.ReportPlaces(new CartoPlaceReportRequest()
            {
                Country   = model.SelectedCountry.ISO2,
                PlaceType = placeType,
                Sort      = "Region"
            });

            return(View(model));
        }
        public GeoPolitical(IGeoPoliticalData data)
        {
            _data = data;

            Timezone = GeoTimezoneInfo.Find(_data.Timezone);
            Country  = GeoCountryInfo.Find(_data.Country);
            Region   = GeoRegionInfo.Find(_data.Region);

            // attempt to backfill timezone
            if (Timezone == null && Region != null)
            {
                Timezone = Graffiti.Geo.GuessTimezone(Region);
            }
            if (Timezone == null && Country != null)
            {
                Timezone = Graffiti.Geo.GuessTimezone(Country);
            }
        }
        public List <GeoCountryInfo> SearchCountries(string name)
        {
            var list = new List <GeoCountryInfo>();

            if (String.IsNullOrWhiteSpace(name))
            {
                return(list);
            }

            var country = GeoCountryInfo.Find(name);

            if (country != null)
            {
                list.Add(country);
                return(list);
            }

            return(GeoCountryInfo.All.Where(x => x.Name.StartsWith(name, StringComparison.InvariantCultureIgnoreCase)).ToList());
        }
        /// <summary>
        /// Displays all of the TopoTrailInfo GPX data files in a list and on a map for a given country with optional region filter
        /// </summary>
        public ActionResult Country(string id, string region, string sort, string tag)
        {
            var model = InitModel();

            model.SelectedSort = sort;

            if (!String.IsNullOrWhiteSpace(region))
            {
                var r = GeoRegionInfo.Find(region);
                if (r == null)
                {
                    model.ErrorMessages.Add($"Invalid region: {region}");
                }
                else
                {
                    model.SelectedRegion  = r;
                    model.SelectedCountry = r.Country;
                    model.Trails          = _trailDataService.ListByRegion(r);
                }
            }
            else
            {
                var c = GeoCountryInfo.Find(id);
                if (c == null)
                {
                    model.ErrorMessages.Add($"Invalid country: {id}");
                }
                else
                {
                    model.SelectedCountry = c;
                    model.Trails          = _trailDataService.ListByCountry(c);
                }
            }

            if (!String.IsNullOrWhiteSpace(tag))
            {
                model.Trails = model.Trails.Where(x => !String.IsNullOrWhiteSpace(x.Keywords) && x.Keywords.ToLowerInvariant().Contains(tag.ToLowerInvariant())).ToList();
            }

            return(View(model));
        }
        public ActionResult Places(int?year, string country = "")
        {
            var model = new OrthoPlacesViewModel();

            model.Years        = _tripSheetService.ListYears();
            model.SelectedYear = year;

            model.Countries       = _tripSheetService.ListCountries();
            model.SelectedCountry = GeoCountryInfo.Find(country);

            var places = new List <OrthoPlaceImportModel>();
            var import = _tripSheetService.ListPlaces(year, country);

            foreach (var data in import)
            {
                var place = new OrthoPlaceImportModel();
                place.Data = data;
                places.Add(place);

                // find existing place by region
                var r = GeoRegionInfo.Find(data.Region);
                if (r != null)
                {
                    place.Place = _cartoPlaceService.FindPlace(r, data.Name, true);
                }

                // find existing place by country
                if (place.Place == null)
                {
                    var c = GeoCountryInfo.Find(data.Country);
                    if (c != null)
                    {
                        place.Place = _cartoPlaceService.FindPlace(c, data.Name, true);
                    }
                }
            }
            model.Places = places;

            return(View(model));
        }
        public List <CartoPlaceData> ListPlaces(int?year = null, string country = "")
        {
            var places = new List <CartoPlaceData>();

            var c          = GeoCountryInfo.Find(country);
            var hasCountry = c != null;

            foreach (var sheet in _source.Sheets)
            {
                var y        = TypeConvert.ToInt(sheet.SheetName);
                var isYear   = (y > 0);
                var hasYear  = year.HasValue;
                var thisYear = y == (year ?? -1);
                if (isYear && (!hasYear || thisYear))
                {
                    foreach (var p in ListRawPlaces(y))
                    {
                        var place = ParsePlace(p);
                        if (place != null && !places.Any(x => IsSamePlace(x, place)))
                        {
                            if (!hasCountry)
                            {
                                places.Add(place);
                            }
                            else
                            {
                                var pc = GeoCountryInfo.Find(place.Country);
                                if (pc != null && pc.CountryID == c.CountryID)
                                {
                                    places.Add(place);
                                }
                            }
                        }
                    }
                }
            }
            return(places);
        }
        /// <summary>
        /// Lists all trails meeting the report request
        /// </summary>
        public List <TopoTrailInfo> Report(TopoTrailReportRequest request)
        {
            var query = _trails.All.AsQueryable();

            if (request.Year.HasValue)
            {
                query = query.Where(x => x.StartLocal.Year == request.Year);
            }
            if (request.Month.HasValue)
            {
                query = query.Where(x => x.StartLocal.Month == request.Month);
            }
            if (request.Day.HasValue)
            {
                query = query.Where(x => x.StartLocal.Day == request.Day);
            }

            if (!String.IsNullOrWhiteSpace(request.Country))
            {
                var country = GeoCountryInfo.Find(request.Country);
                if (country != null)
                {
                    query = query.Where(x => x.Country.CountryID == country.CountryID);
                }
            }

            if (!String.IsNullOrWhiteSpace(request.Region))
            {
                var region = GeoRegionInfo.Find(request.Region);
                if (region != null)
                {
                    query = query.Where(x => x.Region != null && x.Region.RegionID == region.RegionID);
                }
            }

            return(query.ToList());
        }
        /// <summary>
        /// Updates the trail metadata and synchronizes file and cache
        /// </summary>
        public ValidationServiceResponse <TopoTrailInfo> UpdateTrail(ITopoTrailUpdateRequest request)
        {
            // validate update request
            var response = ValidateUpdate(request);

            if (response.HasErrors)
            {
                return(response);
            }

            // process update request
            var trail = response.Data;

            // save current filename for later
            var existing = GetFilename(trail);

            // check file system
            if (!Directory.Exists(_rootUri))
            {
                throw new Exception($"Directory not initalized: {_rootUri}");
            }
            var folder = Path.Combine(_rootUri, trail.Country.Name);

            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }

            // update trail properties
            trail.Timezone = GeoTimezoneInfo.Find(request.Timezone);
            trail.Country  = GeoCountryInfo.Find(request.Country);
            trail.Region   = GeoRegionInfo.Find(request.Region);

            trail.Name        = TextMutate.TrimSafe(request.Name);
            trail.Description = TextMutate.TrimSafe(request.Description);
            trail.Location    = TextMutate.TrimSafe(request.Location);
            trail.Keywords    = TextMutate.FixKeywords(request.Keywords);

            // TODO: BUG: fix url writing for v1.1 files!
            //trail.UrlLink = TextMutate.FixUrl(request.UrlLink);
            //trail.UrlText = TextMutate.TrimSafe(request.UrlText);

            // generate new and rename/remove existing files
            var filename = GetFilename(trail);

            // check if overwrite file
            var renamed = String.Compare(existing, filename, true) != 0;

            if (!renamed)
            {
                // temp rename current file
                File.Move(existing, existing + "~temp");
                existing = existing + "~temp";
            }

            // write new file
            var contents = BuildGpx(trail);

            File.WriteAllText(filename, contents);

            // delete old file
            File.Delete(existing);

            // refresh key and cache data
            if (renamed)
            {
                trail.Key = Path.GetFileNameWithoutExtension(filename).ToUpperInvariant();
                _trails.Remove(request.Key.ToUpperInvariant());
                _trails.Add(trail);
            }

            // return successful response
            return(response);
        }
        /// <summary>
        /// Reports all places which fit the criteria
        /// </summary>
        public List <CartoPlaceInfo> ReportPlaces(CartoPlaceReportRequest request)
        {
            var query = _cache.All.AsQueryable();

            var placeType = request.PlaceType;

            if (!String.IsNullOrWhiteSpace(placeType))
            {
                placeType = placeType.Replace(',', ';').ToLowerInvariant();
                if (placeType.Contains(';'))
                {
                    var placeTypes = placeType.Split(';').Select(x => x.Trim()).ToList();
                    query = query.Where(x => placeTypes.Contains(x.PlaceType.ToLowerInvariant()));
                }
                else
                {
                    query = query.Where(x => (String.Compare(x.PlaceType, placeType, true) == 0));
                }
            }

            var country = GeoCountryInfo.Find(request.Country);

            if (country != null)
            {
                query = query.Where(x => x.Country.CountryID == country.CountryID);
            }

            var region = GeoRegionInfo.Find(request.Region);

            if (region != null)
            {
                query = query.Where(x => x.Region.RegionID == region.RegionID);
            }

            /*
             * var locality = request.Locality;
             * if (!String.IsNullOrWhiteSpace(locality)) query = query.Where(x => String.Compare(x.Locality, locality, true) == 0);
             */

            var name = request.Name;

            if (!String.IsNullOrWhiteSpace(name))
            {
                name = name.Replace("%", "*");
                if (!name.Contains("*"))
                {
                    name = "*" + name + "*";                                      // default to contains search
                }
                var n = name.Trim('*').ToLowerInvariant();
                if (name.StartsWith("*") && name.EndsWith("*"))
                {
                    query = query.Where(x => x.Name.ToLowerInvariant().Contains(n));
                }
                else if (name.EndsWith("*"))
                {
                    query = query.Where(x => x.Name.ToLowerInvariant().EndsWith(n));
                }
                else if (name.StartsWith("*"))
                {
                    query = query.Where(x => x.Name.ToLowerInvariant().StartsWith(n));
                }
                else
                {
                    query = query.Where(x => x.Name.ToLowerInvariant() == n);
                }
            }

            var text = request.Text;

            if (!String.IsNullOrWhiteSpace(text))
            {
                var t = text.ToLowerInvariant();
                query = query.Where(x => GetFullTextSearch(x).Contains(t));
            }

            // apply sort
            if (country == null || region == null)
            {
                query = query.OrderBy(x => x.Country.Name).ThenBy(x => (x.Region == null ? "" : x.Region.RegionName));
            }
            query = query.OrderBy(x => x.Name);

            // return list
            return(query.ToList());
        }