From bd0d56298e2da13d006c001519c2a9c184179916 Mon Sep 17 00:00:00 2001 From: kdt4242 Date: Sun, 23 Jul 2017 13:12:13 -0400 Subject: [PATCH 1/8] Initial commit --- .gitignore | 32 ++++ SlackBotPrototype/.gitignore | 32 ++++ SlackBotPrototype/API.Tests/API.Tests.csproj | 62 ++++++++ SlackBotPrototype/API.Tests/DarkSkyTests.cs | 45 ++++++ .../API.Tests/MessageParserTests.cs | 45 ++++++ .../API.Tests/Properties/AssemblyInfo.cs | 36 +++++ SlackBotPrototype/API.Tests/packages.config | 4 + SlackBotPrototype/API/API.csproj | 148 ++++++++++++++++++ SlackBotPrototype/API/ConfigConstants.cs | 16 ++ .../API/DarkSkyWeatherProvider.cs | 58 +++++++ SlackBotPrototype/API/Extensions.cs | 19 +++ SlackBotPrototype/API/Interfaces.cs | 22 +++ SlackBotPrototype/API/MessageParser.cs | 105 +++++++++++++ SlackBotPrototype/API/Models.cs | 32 ++++ .../API/Properties/AssemblyInfo.cs | 36 +++++ SlackBotPrototype/API/packages.config | 7 + SlackBotPrototype/README.md | 31 ++++ SlackBotPrototype/SlackBotPrototype.sln | 34 ++++ .../SlackBotPrototype/App.config | 14 ++ .../SlackBotPrototype/Program.cs | 105 +++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++ .../SlackBotPrototype.csproj | 70 +++++++++ .../SlackBotPrototype/packages.config | 6 + 23 files changed, 995 insertions(+) create mode 100644 SlackBotPrototype/.gitignore create mode 100644 SlackBotPrototype/API.Tests/API.Tests.csproj create mode 100644 SlackBotPrototype/API.Tests/DarkSkyTests.cs create mode 100644 SlackBotPrototype/API.Tests/MessageParserTests.cs create mode 100644 SlackBotPrototype/API.Tests/Properties/AssemblyInfo.cs create mode 100644 SlackBotPrototype/API.Tests/packages.config create mode 100644 SlackBotPrototype/API/API.csproj create mode 100644 SlackBotPrototype/API/ConfigConstants.cs create mode 100644 SlackBotPrototype/API/DarkSkyWeatherProvider.cs create mode 100644 SlackBotPrototype/API/Extensions.cs create mode 100644 SlackBotPrototype/API/Interfaces.cs create mode 100644 SlackBotPrototype/API/MessageParser.cs create mode 100644 SlackBotPrototype/API/Models.cs create mode 100644 SlackBotPrototype/API/Properties/AssemblyInfo.cs create mode 100644 SlackBotPrototype/API/packages.config create mode 100644 SlackBotPrototype/README.md create mode 100644 SlackBotPrototype/SlackBotPrototype.sln create mode 100644 SlackBotPrototype/SlackBotPrototype/App.config create mode 100644 SlackBotPrototype/SlackBotPrototype/Program.cs create mode 100644 SlackBotPrototype/SlackBotPrototype/Properties/AssemblyInfo.cs create mode 100644 SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj create mode 100644 SlackBotPrototype/SlackBotPrototype/packages.config diff --git a/.gitignore b/.gitignore index 4d64059..e92537f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,34 @@ /node_modules +#Ignore thumbnails created by Windows +Thumbs.db +#Ignore files built by Visual Studio +*.obj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +[Bb]in +[Dd]ebug*/ +*.lib +*.sbr +obj/ +[Rr]elease*/ +_ReSharper*/ +[Tt]est[Rr]esult* +.vs/ +#Nuget packages folder +packages/ + diff --git a/SlackBotPrototype/.gitignore b/SlackBotPrototype/.gitignore new file mode 100644 index 0000000..27b5c94 --- /dev/null +++ b/SlackBotPrototype/.gitignore @@ -0,0 +1,32 @@ + +#Ignore thumbnails created by Windows +Thumbs.db +#Ignore files built by Visual Studio +*.obj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +[Bb]in +[Dd]ebug*/ +*.lib +*.sbr +obj/ +[Rr]elease*/ +_ReSharper*/ +[Tt]est[Rr]esult* +.vs/ +#Nuget packages folder +packages/ diff --git a/SlackBotPrototype/API.Tests/API.Tests.csproj b/SlackBotPrototype/API.Tests/API.Tests.csproj new file mode 100644 index 0000000..1cd6382 --- /dev/null +++ b/SlackBotPrototype/API.Tests/API.Tests.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {BEAB33E8-9F1E-4D39-B741-2069E95834DD} + Library + Properties + API.Tests + API.Tests + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll + + + + + + + + + + + + + + + + + + Designer + + + + + {A234D7B8-EAB9-497F-84E7-354DC066BCB5} + API + + + + \ No newline at end of file diff --git a/SlackBotPrototype/API.Tests/DarkSkyTests.cs b/SlackBotPrototype/API.Tests/DarkSkyTests.cs new file mode 100644 index 0000000..1150ee3 --- /dev/null +++ b/SlackBotPrototype/API.Tests/DarkSkyTests.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace API.Tests +{ + + /// + /// Not a unit test, normally would do this in separate REPL but + /// put here for demonstrative purposes + /// + [TestFixture] + public class TestDarkskyRequests + { + private DarkSkyWeatherProvider _weatherProvider; + + [OneTimeSetUp] + public void Init() + { + _weatherProvider = new DarkSkyWeatherProvider(ConfigConstants.DarkSkyApiSecret); + } + + [Test] + public void TestGetDailyForecast() + { + + var forecast = _weatherProvider.GetForecast(DateTime.Now); + Console.WriteLine(forecast); + Assert.IsNotNull(forecast); + } + + [TestCase("2017/06/01")] + [TestCase("2017/06/05")] + [TestCase("2017/06/10")] + public void TestSummary(string dateStr) + { + var forecast = _weatherProvider.GetForecastRaw(DateTime.Parse(dateStr)); + Console.WriteLine(forecast.Daily.Data.FirstOrDefault()?.Summary); + Assert.IsNotEmpty(forecast.Daily.Data.FirstOrDefault()?.Summary); + } + } +} diff --git a/SlackBotPrototype/API.Tests/MessageParserTests.cs b/SlackBotPrototype/API.Tests/MessageParserTests.cs new file mode 100644 index 0000000..b5ce185 --- /dev/null +++ b/SlackBotPrototype/API.Tests/MessageParserTests.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace API.Tests +{ + + [TestFixture] + public class MessageParserTests + { + private MessageParser _nlpParser; + + [OneTimeSetUp] + public void Init() + { + _nlpParser = new MessageParser(ConfigConstants.StanfordNlpFolder); + } + + [TestCase("weather now", 2)] + [TestCase("weather tomorrow", 2)] + [TestCase("", 0)] + public void TestTokenizer(string message, int count) + { + var wordSet = _nlpParser.BuildWordSet(message); + + Assert.IsTrue(wordSet.Count == count); + } + + [TestCase("weather now")] + [TestCase("weather tomorrow")] + [TestCase("weather today")] + [TestCase("WEATHER NOW!")] + [TestCase("WEATHER TOMORROW")] + [TestCase("What should I wear?")] + public void CommandIntegrationTest(string message) + { + var forecast = _nlpParser.HandleRequest(message); + Console.WriteLine(forecast); + Assert.IsNotEmpty(forecast); + } + } +} diff --git a/SlackBotPrototype/API.Tests/Properties/AssemblyInfo.cs b/SlackBotPrototype/API.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..740a44a --- /dev/null +++ b/SlackBotPrototype/API.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("API.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("API.Tests")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("beab33e8-9f1e-4d39-b741-2069e95834dd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SlackBotPrototype/API.Tests/packages.config b/SlackBotPrototype/API.Tests/packages.config new file mode 100644 index 0000000..cb72ba1 --- /dev/null +++ b/SlackBotPrototype/API.Tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SlackBotPrototype/API/API.csproj b/SlackBotPrototype/API/API.csproj new file mode 100644 index 0000000..bb5b915 --- /dev/null +++ b/SlackBotPrototype/API/API.csproj @@ -0,0 +1,148 @@ + + + + + Debug + AnyCPU + {A234D7B8-EAB9-497F-84E7-354DC066BCB5} + Library + Properties + API + API + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.AWT.WinForms.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Beans.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Charsets.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Cldrdata.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Corba.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Core.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Jdbc.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Localedata.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Management.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Media.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Misc.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Naming.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Nashorn.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Remoting.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Security.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.SwingAWT.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Text.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Tools.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Util.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.API.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Bind.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Crypto.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Parse.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Transform.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.WebServices.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.XPath.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.Runtime.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.Runtime.JNI.dll + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll + + + ..\packages\Stanford.NLP.POSTagger.3.7.0.1\lib\stanford-postagger-3.7.0.dll + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SlackBotPrototype/API/ConfigConstants.cs b/SlackBotPrototype/API/ConfigConstants.cs new file mode 100644 index 0000000..bf229ad --- /dev/null +++ b/SlackBotPrototype/API/ConfigConstants.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace API +{ + public static class ConfigConstants + { + public static readonly string DarkSkyApiSecret = Environment.GetEnvironmentVariable("DARK_SKY_TOKEN"); + public static readonly string SlackApiSecret = Environment.GetEnvironmentVariable("SLACK_API_TOKEN"); + public static readonly string StanfordNlpFolder = Environment.GetEnvironmentVariable("STANFORD_NLP_FOLDER"); + } +} diff --git a/SlackBotPrototype/API/DarkSkyWeatherProvider.cs b/SlackBotPrototype/API/DarkSkyWeatherProvider.cs new file mode 100644 index 0000000..879f9bb --- /dev/null +++ b/SlackBotPrototype/API/DarkSkyWeatherProvider.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using RestSharp; + +namespace API +{ + public class DarkSkyWeatherProvider : IWeatherProvider + { + private readonly string _apiSecret; + private readonly RestClient _client = new RestClient("https://api.darksky.net"); + + public DarkSkyWeatherProvider(string apiSecret) + { + _apiSecret = apiSecret; + } + + public string GetForecast(DateTime when) + { + var forecastItem = GetForecastRaw(when); + return forecastItem.Daily.Data.FirstOrDefault()?.Summary; + } + + public string GetForecastWarning() + { + var today = GetForecastRaw(DateTime.Now); + var yesterday = GetForecastRaw(DateTime.Now.AddDays(-1)); + + // Darksky recommends using "icon" for automated purposes and "summary" for human readable output + return today.Daily.Data.FirstOrDefault()?.Icon != yesterday.Daily.Data.FirstOrDefault()?.Icon + ? today.Daily.Data.FirstOrDefault()?.Summary + : string.Empty; + } + + public ForecastItem GetForecastRaw(DateTime when) + { + var timeStamp = when.DateTimeToUnixTimestamp(); + var resp = _client.Get(new RestRequest(Method.GET) + { + // To save time, hard code location + Resource = $"/forecast/{_apiSecret}/38.9072,-77.0369,{(int)timeStamp}?exclude=currently,minutely,hourly,flags" + }); + + if (resp.StatusCode != HttpStatusCode.OK) + { + return null; + } + + var content = resp.Content; + var forecastItem = JsonConvert.DeserializeObject(content); + return forecastItem; + } + } +} diff --git a/SlackBotPrototype/API/Extensions.cs b/SlackBotPrototype/API/Extensions.cs new file mode 100644 index 0000000..d7c5ddc --- /dev/null +++ b/SlackBotPrototype/API/Extensions.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace API +{ + public static class Extensions + { + + public static double DateTimeToUnixTimestamp(this DateTime dateTime) + { + return (TimeZoneInfo.ConvertTimeToUtc(dateTime) - + new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc)).TotalSeconds; + } + + } +} diff --git a/SlackBotPrototype/API/Interfaces.cs b/SlackBotPrototype/API/Interfaces.cs new file mode 100644 index 0000000..d561529 --- /dev/null +++ b/SlackBotPrototype/API/Interfaces.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace API +{ + /// + /// Internal interface to decouple weather service + /// + public interface IWeatherProvider + { + string GetForecast(DateTime when); + string GetForecastWarning(); + } + + public interface IMessageParser + { + string HandleRequest(string message); + } +} diff --git a/SlackBotPrototype/API/MessageParser.cs b/SlackBotPrototype/API/MessageParser.cs new file mode 100644 index 0000000..1604a4d --- /dev/null +++ b/SlackBotPrototype/API/MessageParser.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Console = System.Console; +using edu.stanford.nlp.ling; +using edu.stanford.nlp.tagger.maxent; + +namespace API +{ + public class MessageParser : IMessageParser + { + private readonly string _modelsDirectory; + + public MessageParser(string nlpFolder) + { + _modelsDirectory = nlpFolder + @"\models"; + } + + public string HandleRequest(string message) + { + var wordSet = BuildWordSet(message); + var weatherProvider = new DarkSkyWeatherProvider(ConfigConstants.DarkSkyApiSecret); + + if (wordSet.Contains("wear", StringComparer.InvariantCultureIgnoreCase) && + wordSet.Contains("I", StringComparer.InvariantCultureIgnoreCase)) + { + var rawForecast = weatherProvider.GetForecastRaw(DateTime.Now); + var icon = rawForecast.Daily.Data.FirstOrDefault()?.Icon; + + // Skip accounting for temperature for now, could have thresholds for certain apparel + switch (icon) + { + case "clear-day": + case "clear-night": + return "Wear your comfortable clothing, no jacket required"; + case "snow": + return "Wear winter jackets, bring boots!"; + case "wind": + return "Bring a wind jacket, it\'s going to breezy"; + case "partly-cloudy-day": + case "partly-cloudy-night": + case "cloudy": + case "fog": + return "Bring a jacket"; + case "rain": + return "Dress comfortably, bring an umbrella"; + + default: + return "I don't know, anything you want?"; + } + } + + if (wordSet.Contains("weather", StringComparer.InvariantCultureIgnoreCase)) + { + if (wordSet.Contains("now", StringComparer.InvariantCultureIgnoreCase) + || wordSet.Contains("today", StringComparer.InvariantCultureIgnoreCase)) + { + return weatherProvider.GetForecast(DateTime.Now); + } + + if (wordSet.Contains("tomorrow")) + { + return weatherProvider.GetForecast(DateTime.Now.AddDays(1)); + } + + return "Weather now or tomorrow?"; + } + + return string.Empty; + } + + public HashSet BuildWordSet(string message) + { + var tagger = new MaxentTagger(_modelsDirectory + @"\wsj-0-18-bidirectional-distsim.tagger"); + + var sentences = MaxentTagger.tokenizeText(new java.io.StringReader(message)).toArray(); + var wordSet = new HashSet(); + + foreach (java.util.ArrayList sentence in sentences) + { + var taggedSentence = tagger.tagSentence(sentence); + + Console.WriteLine(SentenceUtils.listToString(taggedSentence, false)); + + for (var i = 0; i < taggedSentence.size(); i++) + { + var taggedWord = taggedSentence.get(i) as TaggedWord; + + if (taggedWord == null) + continue; + + // could use part of speech and sentence structure as well + var value = taggedWord.value(); + + if (wordSet.Contains(value)) + continue; + + wordSet.Add(value); + } + } + + return wordSet; + } + } +} diff --git a/SlackBotPrototype/API/Models.cs b/SlackBotPrototype/API/Models.cs new file mode 100644 index 0000000..e1707df --- /dev/null +++ b/SlackBotPrototype/API/Models.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace API +{ + + public class ForecastItem + { + [JsonProperty("daily")] + public DailyForecastItem Daily { get; set; } + } + + public class DailyForecastItem + { + [JsonProperty("data")] + public List Data { get; set; } + } + + public class TemporalDataBlock + { + [JsonProperty("summary")] + public string Summary { get; set; } + [JsonProperty("icon")] + public string Icon { get; set; } + [JsonProperty("apparentTemperatureMax")] + public string ApparentTemperatureMax { get; set; } + } +} diff --git a/SlackBotPrototype/API/Properties/AssemblyInfo.cs b/SlackBotPrototype/API/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0d0ac81 --- /dev/null +++ b/SlackBotPrototype/API/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("API")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("API")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a234d7b8-eab9-497f-84e7-354dc066bcb5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SlackBotPrototype/API/packages.config b/SlackBotPrototype/API/packages.config new file mode 100644 index 0000000..be39d9e --- /dev/null +++ b/SlackBotPrototype/API/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/SlackBotPrototype/README.md b/SlackBotPrototype/README.md new file mode 100644 index 0000000..0c7c357 --- /dev/null +++ b/SlackBotPrototype/README.md @@ -0,0 +1,31 @@ +# Weather Slack Bot + +## Overview + +Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it'll notify every morning of the current weather if the today's forecast is different from yesterday's. The bot is currently intended to run a console executable but can be modified to run as a windows service. + +## Development Setup + +* Visual Studio 2017 +* Windows 10 (.NET 4.5.2) + +## External Dependencies + +* DarkSky API -- provides the weather info +* Slack API -- uses RTM inferface +* Stanford NLP library -- used to tokenization and labeling of messages + * Download model data package from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip + +## Setup Instructioons + +* Download Stanford NLP model data from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip +* Extract package contents +* Set environment variable for Stanford NLP model folder `STANFORD_NLP_FOLDER` to the location of the extracted package +* Set environment variable for DarkSky API `DARK_SKY_TOKEN` to DarkSky secret +* Set environment variable for Slack API bot user `SLACK_API_TOKEN` to Slack bot user secret +* Run executable via commandline `./SlackBotProtoType.exe` + + + + + diff --git a/SlackBotPrototype/SlackBotPrototype.sln b/SlackBotPrototype/SlackBotPrototype.sln new file mode 100644 index 0000000..ee81802 --- /dev/null +++ b/SlackBotPrototype/SlackBotPrototype.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.15 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlackBotPrototype", "SlackBotPrototype\SlackBotPrototype.csproj", "{84E25C64-267B-4A86-9EF9-52A550D8B228}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{A234D7B8-EAB9-497F-84E7-354DC066BCB5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Tests", "API.Tests\API.Tests.csproj", "{BEAB33E8-9F1E-4D39-B741-2069E95834DD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {84E25C64-267B-4A86-9EF9-52A550D8B228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84E25C64-267B-4A86-9EF9-52A550D8B228}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84E25C64-267B-4A86-9EF9-52A550D8B228}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84E25C64-267B-4A86-9EF9-52A550D8B228}.Release|Any CPU.Build.0 = Release|Any CPU + {A234D7B8-EAB9-497F-84E7-354DC066BCB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A234D7B8-EAB9-497F-84E7-354DC066BCB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A234D7B8-EAB9-497F-84E7-354DC066BCB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A234D7B8-EAB9-497F-84E7-354DC066BCB5}.Release|Any CPU.Build.0 = Release|Any CPU + {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SlackBotPrototype/SlackBotPrototype/App.config b/SlackBotPrototype/SlackBotPrototype/App.config new file mode 100644 index 0000000..cd9f668 --- /dev/null +++ b/SlackBotPrototype/SlackBotPrototype/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SlackBotPrototype/SlackBotPrototype/Program.cs b/SlackBotPrototype/SlackBotPrototype/Program.cs new file mode 100644 index 0000000..f94669b --- /dev/null +++ b/SlackBotPrototype/SlackBotPrototype/Program.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Threading; +using API; +using FluentScheduler; +using SlackAPI; +using SlackAPI.WebSocketMessages; + +namespace SlackBotPrototype +{ + internal static class Program + { + private static SlackSocketClient _client; + private static IMessageParser _messageParser; + private static IWeatherProvider _weatherProvider; + + //1. Can assume that all requests are for one location(no need to manage individual user location...an instance of the bot can serve "Washington, DC" only). + //2. Respond to two commands triggered upon mention. "Weather now" "Weather tomorrow". They do what you'd expect. + //3. When the weather is going to be materially different from yesterday, let @channel know in the morning. + //4. One embellishment of your choice. Determine a feature you think this bot should have, and implement it. + private static void Main(string[] args) + { + var clientReady = new ManualResetEventSlim(false); + var slackToken = ConfigConstants.SlackApiSecret; + var darkSkyToken = ConfigConstants.DarkSkyApiSecret; + var nlpFolder = ConfigConstants.StanfordNlpFolder; + + if (string.IsNullOrWhiteSpace(slackToken)) + { + // normally would use logging library instead + Console.WriteLine("Slack token not found in environment var name = `{SLACK_API_TOKEN}`"); + return; + } + + if (string.IsNullOrWhiteSpace(nlpFolder)) + { + Console.WriteLine("NLP folder not found in environment var name = `{STANFORD_NLP_FOLDER}`"); + return; + } + + if (string.IsNullOrWhiteSpace(darkSkyToken)) + { + Console.WriteLine("Dark sky API token not available var name = `{DARK_SKY_TOKEN}`"); + return; + } + + _client = new SlackSocketClient(slackToken); + _messageParser = new MessageParser(nlpFolder); + _weatherProvider = new DarkSkyWeatherProvider(darkSkyToken); + + _client.Connect((connected) => { + // This is called once the client has emitted the RTM start command + Console.WriteLine($"RTM started -- {string.Join(",", connected.channels.Select(c => c.name))}"); + clientReady.Set(); + + }, () => { + // This is called once the RTM client has connected to the end point + Console.WriteLine("RTM connected"); + }); + + Channel[] channels; + _client.GetChannelList((response) => + { + channels = response.channels.Where(c => c.is_member).ToArray(); + + JobManager.AddJob(() => + { + var forecastWarning = _weatherProvider.GetForecastWarning(); + + if (channels.Length <= 0 || string.IsNullOrWhiteSpace(forecastWarning)) return; + + foreach (var channel in channels) + { + _client.PostMessage((r) => { }, channel.id, $"Notification: weather changed -- {forecastWarning}"); + } + + }, (s) => s.ToRunEvery(1).Days().At(6, 0)); + }); + + _client.OnMessageReceived += OnClientOnOnMessageReceived; + + clientReady.Wait(); + Console.Read(); + } + + private static void OnClientOnOnMessageReceived(NewMessage message) + { + Console.WriteLine(_client.MyData.name); + Console.WriteLine(message.type + " " + message.text); + + if (message.text.Contains($"@{_client.MyData.id}")) + { + Console.WriteLine("Mentioned"); + var resp = _messageParser.HandleRequest(message.text); + _client.PostMessage((response) => { }, message.channel, !string.IsNullOrWhiteSpace(resp) ? $"{resp}" : "Huh?"); + } + else + { + Console.WriteLine("Not mentioned"); + } + } + } + + +} diff --git a/SlackBotPrototype/SlackBotPrototype/Properties/AssemblyInfo.cs b/SlackBotPrototype/SlackBotPrototype/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f8458bb --- /dev/null +++ b/SlackBotPrototype/SlackBotPrototype/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SlackBotPrototype")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SlackBotPrototype")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("84e25c64-267b-4a86-9ef9-52a550d8b228")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj b/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj new file mode 100644 index 0000000..7357f58 --- /dev/null +++ b/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {84E25C64-267B-4A86-9EF9-52A550D8B228} + Exe + SlackBotPrototype + SlackBotPrototype + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\FluentScheduler.5.3.0\lib\net40\FluentScheduler.dll + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\SlackAPI.1.0.5.93\lib\net45\SlackAPI.dll + + + + + + + + + + + + + + + + + Designer + + + + + + {A234D7B8-EAB9-497F-84E7-354DC066BCB5} + API + + + + \ No newline at end of file diff --git a/SlackBotPrototype/SlackBotPrototype/packages.config b/SlackBotPrototype/SlackBotPrototype/packages.config new file mode 100644 index 0000000..415c695 --- /dev/null +++ b/SlackBotPrototype/SlackBotPrototype/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From eb6b83a96c571ef2a7d49a1f9e8f7d2bdd4eeba7 Mon Sep 17 00:00:00 2001 From: kdt4242 <30329277+kdt4242@users.noreply.github.com> Date: Sun, 23 Jul 2017 13:13:42 -0400 Subject: [PATCH 2/8] Update README.md --- SlackBotPrototype/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SlackBotPrototype/README.md b/SlackBotPrototype/README.md index 0c7c357..76be24c 100644 --- a/SlackBotPrototype/README.md +++ b/SlackBotPrototype/README.md @@ -4,6 +4,10 @@ Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it'll notify every morning of the current weather if the today's forecast is different from yesterday's. The bot is currently intended to run a console executable but can be modified to run as a windows service. +## Features + +* Responds to `weather now`, `weather tomorrow`, and `what should I wear?` + ## Development Setup * Visual Studio 2017 @@ -23,7 +27,7 @@ Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it' * Set environment variable for Stanford NLP model folder `STANFORD_NLP_FOLDER` to the location of the extracted package * Set environment variable for DarkSky API `DARK_SKY_TOKEN` to DarkSky secret * Set environment variable for Slack API bot user `SLACK_API_TOKEN` to Slack bot user secret -* Run executable via commandline `./SlackBotProtoType.exe` +* Run executable via commandline `./SlackBotPrototype.exe` From 6ca913e21bc4ffee8b49909160c45afae0c9b4ff Mon Sep 17 00:00:00 2001 From: kdt4242 <30329277+kdt4242@users.noreply.github.com> Date: Sun, 23 Jul 2017 13:18:54 -0400 Subject: [PATCH 3/8] Update README.md --- SlackBotPrototype/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SlackBotPrototype/README.md b/SlackBotPrototype/README.md index 76be24c..e274fbd 100644 --- a/SlackBotPrototype/README.md +++ b/SlackBotPrototype/README.md @@ -20,7 +20,7 @@ Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it' * Stanford NLP library -- used to tokenization and labeling of messages * Download model data package from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip -## Setup Instructioons +## Setup Instructions * Download Stanford NLP model data from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip * Extract package contents From 8860a00ce45d622d397e6f8e94c51eb4856b1c7a Mon Sep 17 00:00:00 2001 From: kdt4242 <30329277+kdt4242@users.noreply.github.com> Date: Sun, 23 Jul 2017 13:21:00 -0400 Subject: [PATCH 4/8] Update README.md --- SlackBotPrototype/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SlackBotPrototype/README.md b/SlackBotPrototype/README.md index e274fbd..16d8048 100644 --- a/SlackBotPrototype/README.md +++ b/SlackBotPrototype/README.md @@ -17,7 +17,7 @@ Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it' * DarkSky API -- provides the weather info * Slack API -- uses RTM inferface -* Stanford NLP library -- used to tokenization and labeling of messages +* Stanford NLP library -- used for tokenization and labeling of messages * Download model data package from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip ## Setup Instructions From 80f5cfa677a7e66c95fee294562dcb4b02098f42 Mon Sep 17 00:00:00 2001 From: kdt4242 Date: Sun, 23 Jul 2017 16:36:18 -0400 Subject: [PATCH 5/8] Add @channel mention and name linking --- SlackBotPrototype/SlackBotPrototype/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SlackBotPrototype/SlackBotPrototype/Program.cs b/SlackBotPrototype/SlackBotPrototype/Program.cs index f94669b..59abc39 100644 --- a/SlackBotPrototype/SlackBotPrototype/Program.cs +++ b/SlackBotPrototype/SlackBotPrototype/Program.cs @@ -71,7 +71,7 @@ private static void Main(string[] args) foreach (var channel in channels) { - _client.PostMessage((r) => { }, channel.id, $"Notification: weather changed -- {forecastWarning}"); + _client.PostMessage((r) => { }, channel.id, $"@channel Notification: weather changed -- {forecastWarning}", linkNames: true); } }, (s) => s.ToRunEvery(1).Days().At(6, 0)); From b59226922f1cc65bb574312cf996999f9dc8f958 Mon Sep 17 00:00:00 2001 From: kdt4242 <30329277+kdt4242@users.noreply.github.com> Date: Sun, 6 Aug 2017 08:40:26 -0400 Subject: [PATCH 6/8] Rework from feedback (#1) * Rework w/ new requirement and feedback --- .gitignore | 3 + SlackBotPrototype/API.Tests/API.Tests.csproj | 10 + SlackBotPrototype/API.Tests/CommandTests.cs | 122 + SlackBotPrototype/API.Tests/DarkSkyTests.cs | 37 +- .../API.Tests/EmbeddedResourceTestBase.cs | 36 + .../API.Tests/LocationServiceTests.cs | 52 + .../API.Tests/MessageParserTests.cs | 65 +- .../API.Tests/TestData/dc-daily.json | 258 + SlackBotPrototype/API.Tests/packages.config | 2 + SlackBotPrototype/API/API.csproj | 35 +- SlackBotPrototype/API/Commands.cs | 123 + SlackBotPrototype/API/ConfigConstants.cs | 7 +- .../API/DarkSkyWeatherProvider.cs | 56 +- SlackBotPrototype/API/Extensions.cs | 6 +- SlackBotPrototype/API/ExternalModels.cs | 32 + SlackBotPrototype/API/Interfaces.cs | 57 +- SlackBotPrototype/API/LocationService.cs | 24 + .../API/MessageToCommandConverter.cs | 185 + SlackBotPrototype/API/Models.cs | 84 +- .../API/Properties/AssemblyInfo.cs | 1 - .../API/RestClientHttpProvider.cs | 26 + SlackBotPrototype/API/SqLitePersistence.cs | 84 + SlackBotPrototype/API/packages.config | 7 + .../DBPreparer/DBPreparer.csproj | 93 + SlackBotPrototype/DBPreparer/Program.cs | 37 + .../DBPreparer/Properties/AssemblyInfo.cs | 36 + .../DBPreparer/locations-seed.json | 9002 +++++++++++++++++ SlackBotPrototype/README.md | 48 +- SlackBotPrototype/SlackBotPrototype.sln | 6 + .../SlackBotPrototype/App.config | 14 - .../SlackBotPrototype/Program.cs | 50 +- .../SlackBotPrototype.csproj | 87 + .../SlackBotPrototype/packages.config | 2 + 33 files changed, 10518 insertions(+), 169 deletions(-) create mode 100644 SlackBotPrototype/API.Tests/CommandTests.cs create mode 100644 SlackBotPrototype/API.Tests/EmbeddedResourceTestBase.cs create mode 100644 SlackBotPrototype/API.Tests/LocationServiceTests.cs create mode 100644 SlackBotPrototype/API.Tests/TestData/dc-daily.json create mode 100644 SlackBotPrototype/API/Commands.cs create mode 100644 SlackBotPrototype/API/ExternalModels.cs create mode 100644 SlackBotPrototype/API/LocationService.cs create mode 100644 SlackBotPrototype/API/MessageToCommandConverter.cs create mode 100644 SlackBotPrototype/API/RestClientHttpProvider.cs create mode 100644 SlackBotPrototype/API/SqLitePersistence.cs create mode 100644 SlackBotPrototype/DBPreparer/DBPreparer.csproj create mode 100644 SlackBotPrototype/DBPreparer/Program.cs create mode 100644 SlackBotPrototype/DBPreparer/Properties/AssemblyInfo.cs create mode 100644 SlackBotPrototype/DBPreparer/locations-seed.json delete mode 100644 SlackBotPrototype/SlackBotPrototype/App.config diff --git a/.gitignore b/.gitignore index e92537f..d141099 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ _ReSharper*/ #Nuget packages folder packages/ + +*.config +SlackBotPrototype/SlackBotPrototype/App.config \ No newline at end of file diff --git a/SlackBotPrototype/API.Tests/API.Tests.csproj b/SlackBotPrototype/API.Tests/API.Tests.csproj index 1cd6382..c35b6b5 100644 --- a/SlackBotPrototype/API.Tests/API.Tests.csproj +++ b/SlackBotPrototype/API.Tests/API.Tests.csproj @@ -30,6 +30,12 @@ 4 + + ..\packages\Castle.Core.4.1.1\lib\net45\Castle.Core.dll + + + ..\packages\Moq.4.7.99\lib\net45\Moq.dll + ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll @@ -43,7 +49,10 @@ + + + @@ -51,6 +60,7 @@ Designer + diff --git a/SlackBotPrototype/API.Tests/CommandTests.cs b/SlackBotPrototype/API.Tests/CommandTests.cs new file mode 100644 index 0000000..883ac22 --- /dev/null +++ b/SlackBotPrototype/API.Tests/CommandTests.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; + +namespace API.Tests +{ + + [TestFixture] + public class CommandTests : EmbeddedResourceTestBase + { + private static readonly Location TestLocation = + new Location {Latitude = 38.889931, Longitude = -77.009003, DisplayName = "Washington, DC"}; + + private IWeatherProvider _weatherProvider; + private IRepository _repository; + + [OneTimeSetUp] + public void Init() + { + var mockHttp = new Mock(); + + var dailyForecastJson = GetFromResource("dc-daily.json"); + mockHttp.Setup(m => m.GetContent(It.IsAny())).Returns(dailyForecastJson); + _weatherProvider = new DarkSkyWeatherProvider(ConfigConstants.DarkSkyApiSecret, mockHttp.Object); + + var mockRepo = new Mock(); + mockRepo.Setup(m => m.GetLocation("San Francisco", "CA")) + .Returns(new Location() { City = "San Francisco", State = "CA", DisplayName = "San Francisco, CA" }); + + _repository = mockRepo.Object; + } + + [Test] + public void TestLocationInvalidWeatherCommand() + { + + var location = new Location() + { + Latitude = 0, + Longitude = 0 + }; + + var when = DateTime.Parse("2017/07/01"); + + var weatherCommand = new WeatherNowCommand(_weatherProvider, location, when); + var msg = weatherCommand.Invoke(); + + // SHORTCUT: A bit of shortcut here, normally we'd define a + // some sort of reponse code enum and test based on the response code. + // Since we don't return a result code right now (requires more boilerplate), just test based on expected string. + Assert.IsTrue(msg.Contains("No location specified")); + } + + [Test] + public void TestValidLocationWeatherCommand() + { + var when = DateTime.Parse("2017/07/01"); + + var weatherCommand = new WeatherNowCommand(_weatherProvider, TestLocation, when); + var msg = weatherCommand.Invoke(); + + // we know that we always include the city in weather results, this would have + // to be updated as things change + Assert.IsTrue(msg.Contains("Washington, DC")); + } + + [Test] + public void TestSetLocationCommand() + { + var location = new Location(); + + var setLocationCommand = new SetUserLocationCommand(_repository, "Test", "123", location); + var msg = setLocationCommand.Invoke(); + + Assert.IsTrue(msg.Contains("Location invalid")); + } + + [Test] + public void TestSetValidLocationCommand() + { + var setLocationCommand = new SetUserLocationCommand(_repository, "Test", "123", TestLocation); + + // Don't think there's much of a point to testing the repository inserts since + // without setting up an integration test with SQLite + var msg = setLocationCommand.Invoke(); + + Assert.IsTrue(msg.Contains("Set location")); + } + + [Test] + public void TestSetNullLocation() + { + var setLocationCommand = new SetUserLocationCommand(_repository, "Test", "123", null); + + // Don't think there's much of a point to testing the repository inserts since + // without setting up an integration test with SQLite + var msg = setLocationCommand.Invoke(); + + Assert.IsTrue(msg.Contains("Location not found")); + } + + [TestCase(ExpectedResult = "")] + public string TestNoopCommand() + { + var noop = new NoOpCommand(); + return noop.Invoke(); + } + + [Test] + public void TestWhatToWearCommand() + { + var whatToWear = new WhatToWearCommand(_weatherProvider); + var msg = whatToWear.Invoke(); + + Assert.IsTrue(msg == "Bring a jacket"); + } + } +} diff --git a/SlackBotPrototype/API.Tests/DarkSkyTests.cs b/SlackBotPrototype/API.Tests/DarkSkyTests.cs index 1150ee3..4bb0f2c 100644 --- a/SlackBotPrototype/API.Tests/DarkSkyTests.cs +++ b/SlackBotPrototype/API.Tests/DarkSkyTests.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; +using Moq; using NUnit.Framework; namespace API.Tests @@ -13,33 +16,39 @@ namespace API.Tests /// put here for demonstrative purposes /// [TestFixture] - public class TestDarkskyRequests + public class TestDarkskyRequests : EmbeddedResourceTestBase { - private DarkSkyWeatherProvider _weatherProvider; + private Location _testLocation; + private IWeatherProvider _weatherProvider; [OneTimeSetUp] public void Init() { - _weatherProvider = new DarkSkyWeatherProvider(ConfigConstants.DarkSkyApiSecret); + _testLocation = new Location() { Latitude = 38.889931, Longitude = -77.0369 }; + + var mockHttp = new Mock(); + + var dcDailyForecastJson = GetFromResource("dc-daily.json"); + mockHttp.Setup(m => m.GetContent(It.IsRegex("-77.0369"))).Returns(dcDailyForecastJson); + _weatherProvider = new DarkSkyWeatherProvider(ConfigConstants.DarkSkyApiSecret, mockHttp.Object); } [Test] public void TestGetDailyForecast() { - - var forecast = _weatherProvider.GetForecast(DateTime.Now); - Console.WriteLine(forecast); + var forecast = _weatherProvider.GetForecast(_testLocation, DateTime.Now); Assert.IsNotNull(forecast); } - - [TestCase("2017/06/01")] - [TestCase("2017/06/05")] - [TestCase("2017/06/10")] - public void TestSummary(string dateStr) + + [Test] + public void TestSummary() { - var forecast = _weatherProvider.GetForecastRaw(DateTime.Parse(dateStr)); - Console.WriteLine(forecast.Daily.Data.FirstOrDefault()?.Summary); - Assert.IsNotEmpty(forecast.Daily.Data.FirstOrDefault()?.Summary); + var forecast = _weatherProvider.GetForecast(_testLocation, DateTime.Now); + Assert.IsTrue(forecast == "Partly cloudy starting in the evening."); } + + // SHORTCUT: Skip testing for time based weather requests, we'd need to mock out + // the requests which doesn't really help testing the temporal component of the weather + // requests as those are really mainly handled by DarkSky } } diff --git a/SlackBotPrototype/API.Tests/EmbeddedResourceTestBase.cs b/SlackBotPrototype/API.Tests/EmbeddedResourceTestBase.cs new file mode 100644 index 0000000..17e66f7 --- /dev/null +++ b/SlackBotPrototype/API.Tests/EmbeddedResourceTestBase.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace API.Tests +{ + public class EmbeddedResourceTestBase + { + + protected string GetFromResource(string fileName) + { + if (fileName == null) + throw new ArgumentNullException(nameof(fileName)); + + var assembly = Assembly.GetExecutingAssembly(); + var resourceName = assembly.GetManifestResourceNames().First(x => x.Contains(fileName)); + + using (var file = assembly.GetManifestResourceStream(resourceName)) + { + if (file == null) + Assert.Fail(); + + using (var sr = new StreamReader(file)) + { + var json = sr.ReadToEnd(); + return json; + } + } + } + } +} diff --git a/SlackBotPrototype/API.Tests/LocationServiceTests.cs b/SlackBotPrototype/API.Tests/LocationServiceTests.cs new file mode 100644 index 0000000..eb6177d --- /dev/null +++ b/SlackBotPrototype/API.Tests/LocationServiceTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; + +namespace API.Tests +{ + + [TestFixture] + public class LocationServiceTests + { + // SHORTCUT: We wouldn't normally be able to test a + // location service like this since it'd probably involve a service call. We'd need to mock + // it in some way. + private LocationService _locationService; + + [OneTimeSetUp] + public void Init() + { + // Not much of a point to mock up (test) DB retrieval since there's really no logic in repository + // at this moment + var repository = new Mock(); + _locationService = new LocationService(repository.Object); + } + + [TestCase("Invalid")] + [TestCase("")] + [TestCase(null)] + public void TestInvalidLocation(string name) + { + var location = new Location() + { + DisplayName = name + }; + + Assert.IsTrue(location.IsInvalid(), "location.IsInvalid() != true"); + } + + // Since we don't have an integrated test setup with SQLite + // there's not really a point to testing retrieval from a mock object as there's not + // much logic in the retrieval code. Just verify that we handle nulls correctly. + [TestCase(100, ExpectedResult = null)] + [TestCase(-1, ExpectedResult = null)] + public Location TestGetLocationById(int id) + { + return _locationService.GetLocation(id); + } + } +} diff --git a/SlackBotPrototype/API.Tests/MessageParserTests.cs b/SlackBotPrototype/API.Tests/MessageParserTests.cs index b5ce185..05c06b8 100644 --- a/SlackBotPrototype/API.Tests/MessageParserTests.cs +++ b/SlackBotPrototype/API.Tests/MessageParserTests.cs @@ -3,43 +3,64 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Moq; using NUnit.Framework; namespace API.Tests { [TestFixture] - public class MessageParserTests + public class MessageParserTests : EmbeddedResourceTestBase { - private MessageParser _nlpParser; + private MessageToCommandConverter _messageToCommandConverter; [OneTimeSetUp] public void Init() { - _nlpParser = new MessageParser(ConfigConstants.StanfordNlpFolder); - } + var mockHttp = new Mock(); - [TestCase("weather now", 2)] - [TestCase("weather tomorrow", 2)] - [TestCase("", 0)] - public void TestTokenizer(string message, int count) - { - var wordSet = _nlpParser.BuildWordSet(message); + var dailyForecastJson = GetFromResource("dc-daily.json"); + mockHttp.Setup(m => m.GetContent(It.IsAny())).Returns(dailyForecastJson); - Assert.IsTrue(wordSet.Count == count); - } + var weatherProvider = new DarkSkyWeatherProvider(ConfigConstants.DarkSkyApiSecret, mockHttp.Object); - [TestCase("weather now")] - [TestCase("weather tomorrow")] - [TestCase("weather today")] - [TestCase("WEATHER NOW!")] - [TestCase("WEATHER TOMORROW")] - [TestCase("What should I wear?")] - public void CommandIntegrationTest(string message) + var mockRepo = new Mock(); + mockRepo.Setup(m => m.GetLocation("San Francisco", "CA")) + .Returns(new Location() {City = "San Francisco", State = "CA", DisplayName = "San Francisco, CA"}); + mockRepo.Setup(m => m.GetLocation("new york", "ny")) + .Returns(new Location() { City = "New York", State = "NY", DisplayName = "New York, NY" }); + mockRepo.Setup(m => m.GetLocation("New York", "NY")) + .Returns(new Location() { City = "New York", State = "NY", DisplayName = "New York, NY" }); + + _messageToCommandConverter = new MessageToCommandConverter(weatherProvider, mockRepo.Object); + } + + [TestCase("<@U6BQE1XGD> weather now", typeof(WeatherNowCommand))] + [TestCase("<@U6BQE1XGD> weather tomorrow", typeof(WeatherNowCommand))] + [TestCase("<@U6BQE1XGD> weather today", typeof(InvalidCommand))] + [TestCase("<@U6BQE1XGD> WEATHER NOW!", typeof(NoOpCommand))] + [TestCase("<@U6BQE1XGD> WEATHER TOMORROW", typeof(WeatherNowCommand))] + [TestCase("<@U6BQE1XGD> What should I wear?", typeof(WhatToWearCommand))] + [TestCase("<@U6BQE1XGD> Set me to San Fransisco, CA", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> Set me to new york, ny", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> Set me to Washington, DC", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> Set me to Nowheresville123, NY", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> Weather now San Francisco, CA", typeof(WeatherNowCommand))] + [TestCase("<@U6BQE1XGD> set me to new york, ny", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> set me to Unrecognizable location, OkayCity", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> set me to Unrecognizable location!!! !!, OkayCity", typeof(SetUserLocationCommand))] + [TestCase("<@U6BQE1XGD> Weather now New York, NY", typeof(WeatherNowCommand))] + [TestCase("<@U6BQE1XGD> Hello", typeof(NoOpCommand))] + [TestCase("set me to new york, ny", typeof(NoOpCommand))] + [TestCase("weather now", typeof(NoOpCommand))] + [TestCase("", typeof(NoOpCommand))] + [TestCase(null, typeof(NoOpCommand))] + public void VerifyCommands(string message, Type expectedType) { - var forecast = _nlpParser.HandleRequest(message); - Console.WriteLine(forecast); - Assert.IsNotEmpty(forecast); + var resp = _messageToCommandConverter.HandleRequest("test", message); + var command = resp.Command; + + Assert.IsTrue(expectedType == command.GetType(), $"command did not match expected for message: {message} --> {command.GetType()}"); } } } diff --git a/SlackBotPrototype/API.Tests/TestData/dc-daily.json b/SlackBotPrototype/API.Tests/TestData/dc-daily.json new file mode 100644 index 0000000..f7f9ec2 --- /dev/null +++ b/SlackBotPrototype/API.Tests/TestData/dc-daily.json @@ -0,0 +1,258 @@ +{ + "latitude": 38.9072, + "longitude": -77.0369, + "timezone": "America/New_York", + "offset": -4, + "daily": { + "summary": "Light rain on Monday through next Friday, with temperatures falling to 73°F next Friday.", + "icon": "rain", + "data": [{ + "time": 1501819200, + "summary": "Partly cloudy starting in the evening.", + "icon": "partly-cloudy-night", + "sunriseTime": 1501841593, + "sunsetTime": 1501892265, + "moonPhase": 0.4, + "precipIntensity": 0.0007, + "precipIntensityMax": 0.0038, + "precipIntensityMaxTime": 1501902000, + "precipProbability": 0.11, + "precipType": "rain", + "temperatureMin": 68.5, + "temperatureMinTime": 1501840800, + "temperatureMax": 89.4, + "temperatureMaxTime": 1501873200, + "apparentTemperatureMin": 69.49, + "apparentTemperatureMinTime": 1501840800, + "apparentTemperatureMax": 93.91, + "apparentTemperatureMaxTime": 1501866000, + "dewPoint": 67.87, + "humidity": 0.73, + "windSpeed": 3.83, + "windGust": 18.95, + "windGustTime": 1501891200, + "windBearing": 169, + "visibility": 9.95, + "cloudCover": 0.12, + "pressure": 1014.69, + "ozone": 307.17, + "uvIndex": 9, + "uvIndexTime": 1501866000 + }, { + "time": 1501905600, + "summary": "Mostly cloudy until afternoon.", + "icon": "partly-cloudy-day", + "sunriseTime": 1501928047, + "sunsetTime": 1501978600, + "moonPhase": 0.43, + "precipIntensity": 0.0005, + "precipIntensityMax": 0.0035, + "precipIntensityMaxTime": 1501912800, + "precipProbability": 0.06, + "precipType": "rain", + "temperatureMin": 69.22, + "temperatureMinTime": 1501988400, + "temperatureMax": 79.33, + "temperatureMaxTime": 1501966800, + "apparentTemperatureMin": 69.22, + "apparentTemperatureMinTime": 1501988400, + "apparentTemperatureMax": 79.33, + "apparentTemperatureMaxTime": 1501966800, + "dewPoint": 59.32, + "humidity": 0.61, + "windSpeed": 7.69, + "windGust": 21.69, + "windGustTime": 1501938000, + "windBearing": 298, + "cloudCover": 0.44, + "pressure": 1014.64, + "ozone": 311.44, + "uvIndex": 7, + "uvIndexTime": 1501952400 + }, { + "time": 1501992000, + "summary": "Light rain overnight.", + "icon": "rain", + "sunriseTime": 1502014502, + "sunsetTime": 1502064934, + "moonPhase": 0.46, + "precipIntensity": 0, + "precipIntensityMax": 0, + "precipProbability": 0, + "temperatureMin": 63.71, + "temperatureMinTime": 1502010000, + "temperatureMax": 81.53, + "temperatureMaxTime": 1502053200, + "apparentTemperatureMin": 63.71, + "apparentTemperatureMinTime": 1502010000, + "apparentTemperatureMax": 81.53, + "apparentTemperatureMaxTime": 1502053200, + "dewPoint": 53.96, + "humidity": 0.53, + "windSpeed": 1.46, + "windGust": 15.99, + "windGustTime": 1502074800, + "windBearing": 195, + "cloudCover": 0.48, + "pressure": 1020.12, + "ozone": 300.61, + "uvIndex": 6, + "uvIndexTime": 1502038800 + }, { + "time": 1502078400, + "summary": "Rain throughout the day.", + "icon": "rain", + "sunriseTime": 1502100957, + "sunsetTime": 1502151267, + "moonPhase": 0.49, + "precipIntensity": 0.029, + "precipIntensityMax": 0.0685, + "precipIntensityMaxTime": 1502118000, + "precipProbability": 0.88, + "precipType": "rain", + "temperatureMin": 69.32, + "temperatureMinTime": 1502103600, + "temperatureMax": 76.38, + "temperatureMaxTime": 1502143200, + "apparentTemperatureMin": 69.65, + "apparentTemperatureMinTime": 1502103600, + "apparentTemperatureMax": 77.43, + "apparentTemperatureMaxTime": 1502143200, + "dewPoint": 65.55, + "humidity": 0.79, + "windSpeed": 5.67, + "windGust": 21.82, + "windGustTime": 1502118000, + "windBearing": 181, + "cloudCover": 0.92, + "pressure": 1016.01, + "ozone": 290.12, + "uvIndex": 6, + "uvIndexTime": 1502125200 + }, { + "time": 1502164800, + "summary": "Mostly cloudy throughout the day.", + "icon": "partly-cloudy-day", + "sunriseTime": 1502187412, + "sunsetTime": 1502237598, + "moonPhase": 0.53, + "precipIntensity": 0.0009, + "precipIntensityMax": 0.016, + "precipIntensityMaxTime": 1502164800, + "precipProbability": 0.28, + "precipType": "rain", + "temperatureMin": 68.09, + "temperatureMinTime": 1502193600, + "temperatureMax": 78.45, + "temperatureMaxTime": 1502226000, + "apparentTemperatureMin": 68.52, + "apparentTemperatureMinTime": 1502247600, + "apparentTemperatureMax": 78.59, + "apparentTemperatureMaxTime": 1502226000, + "dewPoint": 63, + "humidity": 0.73, + "windSpeed": 5.36, + "windGust": 16.38, + "windGustTime": 1502175600, + "windBearing": 350, + "cloudCover": 0.88, + "pressure": 1016.18, + "ozone": 299.76, + "uvIndex": 5, + "uvIndexTime": 1502208000 + }, { + "time": 1502251200, + "summary": "Mostly cloudy throughout the day.", + "icon": "partly-cloudy-day", + "sunriseTime": 1502273867, + "sunsetTime": 1502323928, + "moonPhase": 0.56, + "precipIntensity": 0.0004, + "precipIntensityMax": 0.0018, + "precipIntensityMaxTime": 1502269200, + "precipProbability": 0.01, + "precipType": "rain", + "temperatureMin": 66.31, + "temperatureMinTime": 1502258400, + "temperatureMax": 79.17, + "temperatureMaxTime": 1502312400, + "apparentTemperatureMin": 66.31, + "apparentTemperatureMinTime": 1502258400, + "apparentTemperatureMax": 79.17, + "apparentTemperatureMaxTime": 1502312400, + "dewPoint": 57.95, + "humidity": 0.62, + "windSpeed": 4.04, + "windGust": 15.71, + "windGustTime": 1502334000, + "windBearing": 94, + "cloudCover": 0.47, + "pressure": 1022.09, + "ozone": 308.79, + "uvIndex": 6, + "uvIndexTime": 1502298000 + }, { + "time": 1502337600, + "summary": "Light rain in the morning and afternoon.", + "icon": "rain", + "sunriseTime": 1502360322, + "sunsetTime": 1502410256, + "moonPhase": 0.59, + "precipIntensity": 0.0113, + "precipIntensityMax": 0.0339, + "precipIntensityMaxTime": 1502406000, + "precipProbability": 0.44, + "precipType": "rain", + "temperatureMin": 66.88, + "temperatureMinTime": 1502341200, + "temperatureMax": 79.46, + "temperatureMaxTime": 1502388000, + "apparentTemperatureMin": 67.03, + "apparentTemperatureMinTime": 1502341200, + "apparentTemperatureMax": 81.4, + "apparentTemperatureMaxTime": 1502388000, + "dewPoint": 64.56, + "humidity": 0.76, + "windSpeed": 4.8, + "windGust": 22.06, + "windGustTime": 1502366400, + "windBearing": 130, + "cloudCover": 0.85, + "pressure": 1018.99, + "ozone": 290.2, + "uvIndex": 6, + "uvIndexTime": 1502384400 + }, { + "time": 1502424000, + "summary": "Rain throughout the day.", + "icon": "rain", + "sunriseTime": 1502446777, + "sunsetTime": 1502496584, + "moonPhase": 0.63, + "precipIntensity": 0.0244, + "precipIntensityMax": 0.0668, + "precipIntensityMaxTime": 1502442000, + "precipProbability": 0.6, + "precipType": "rain", + "temperatureMin": 68.63, + "temperatureMinTime": 1502452800, + "temperatureMax": 72.62, + "temperatureMaxTime": 1502467200, + "apparentTemperatureMin": 69.4, + "apparentTemperatureMinTime": 1502452800, + "apparentTemperatureMax": 73.42, + "apparentTemperatureMaxTime": 1502467200, + "dewPoint": 67.07, + "humidity": 0.89, + "windSpeed": 3.96, + "windGust": 10.47, + "windGustTime": 1502488800, + "windBearing": 83, + "cloudCover": 0.87, + "pressure": 1016.08, + "ozone": 287.07, + "uvIndex": 6, + "uvIndexTime": 1502470800 + }] + } +} \ No newline at end of file diff --git a/SlackBotPrototype/API.Tests/packages.config b/SlackBotPrototype/API.Tests/packages.config index cb72ba1..eaa6c5c 100644 --- a/SlackBotPrototype/API.Tests/packages.config +++ b/SlackBotPrototype/API.Tests/packages.config @@ -1,4 +1,6 @@  + + \ No newline at end of file diff --git a/SlackBotPrototype/API/API.csproj b/SlackBotPrototype/API/API.csproj index bb5b915..69306a3 100644 --- a/SlackBotPrototype/API/API.csproj +++ b/SlackBotPrototype/API/API.csproj @@ -11,6 +11,8 @@ API v4.5.2 512 + + true @@ -120,6 +122,21 @@ ..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll + + ..\packages\sqlite-net-pcl.1.5.151-beta\lib\netstandard1.1\SQLite-net.dll + + + ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll + + + ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll + + + ..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll + + + ..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll + ..\packages\Stanford.NLP.POSTagger.3.7.0.1\lib\stanford-postagger-3.7.0.dll @@ -136,13 +153,29 @@ - + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/SlackBotPrototype/API/Commands.cs b/SlackBotPrototype/API/Commands.cs new file mode 100644 index 0000000..7eecb63 --- /dev/null +++ b/SlackBotPrototype/API/Commands.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace API +{ + public class WeatherNowCommand : ICommandImpl + { + private readonly IWeatherProvider _weatherProvider; + private readonly Location _location; + private readonly DateTime _when; + + // SHORTCUT: Could set up dependency injection here instead of using constructors. + // We need to pass in dependencies for commands to make them easier to test + public WeatherNowCommand(IWeatherProvider weatherProvider, Location location, DateTime when) + { + _weatherProvider = weatherProvider; + _location = location; + _when = when; + } + + public string Invoke() + { + return _location.IsInvalid() + ? "No location specified. Specify location or set your location with `set me to City, State`, eg. `set me to new york, ny" + : $"{_location.DisplayName}: {_weatherProvider.GetForecast(_location, _when)}"; + } + } + + public class WhatToWearCommand : ICommandImpl + { + private readonly IWeatherProvider _weatherProvider; + + // SHORTCUT: We could choose to keep this lookup in a configuration file, or in persistence layer (DB). + // This would allow us to update the mapping without having to deploy code. + private readonly IDictionary _weatherToClothingLookup = new Dictionary + { + {"clear-day", "Wear your comfortable clothing, no jacket required"}, + {"clear-night", "Wear your comfortable clothing, no jacket required"}, + {"snow", "Wear winter jackets, bring boots!"}, + {"wind", "Bring a wind jacket, it\'s going to breezy"}, + {"partly-cloudy-day","Bring a jacket"}, + {"partly-cloudy-night","Bring a jacket"}, + {"cloudy","Bring a jacket"}, + {"fog","Bring a jacket"}, + {"rain", "Dress comfortably, bring an umbrella"} + }; + + public WhatToWearCommand(IWeatherProvider weatherProvider) + { + _weatherProvider = weatherProvider; + } + + public string Invoke() + { + // SHORTCUT: Not implementing location support for this command to save time, it's not in scope of project. + // Just use a hard coded location. + var dcLocation = new Location {Latitude = 38.889931, Longitude = -77.009003, DisplayName = "Washington, DC"}; + var summaryIcon = _weatherProvider.GetForecastRaw(dcLocation, DateTime.Now).Icon; + return _weatherToClothingLookup.TryGetValue(summaryIcon, out string recommendation) ? recommendation : "I don't know, anything you want?"; + } + } + + public class SetUserLocationCommand : ICommandImpl + { + private readonly IRepository _repository; + private readonly string _name; + private readonly string _slackId; + private readonly Location _location; + + // We need to pass in dependencies for commands to make them easier to test + public SetUserLocationCommand(IRepository repository, string name, string slackId, Location location) + { + _repository = repository; + _name = name; + _slackId = slackId; + _location = location; + } + + public string Invoke() + { + if (_location == null) + { + return "Location not found"; + } + + if (_location.IsInvalid()) + { + return "Location invalid"; + } + + _repository.CreateOrUpdate(_name, _slackId, _location); + return $"Set location to: {_location.DisplayName}"; + } + } + + public class InvalidCommand : ICommandImpl + { + private readonly string _message; + + public InvalidCommand() + { + } + + public InvalidCommand(string message) + { + _message = message; + } + + public string Invoke() + { + return _message ?? "Huh?"; + } + } + + public class NoOpCommand : ICommandImpl + { + public string Invoke() + { + return string.Empty; + } + } +} diff --git a/SlackBotPrototype/API/ConfigConstants.cs b/SlackBotPrototype/API/ConfigConstants.cs index bf229ad..f8389f7 100644 --- a/SlackBotPrototype/API/ConfigConstants.cs +++ b/SlackBotPrototype/API/ConfigConstants.cs @@ -1,9 +1,5 @@ using System; using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace API { @@ -11,6 +7,7 @@ public static class ConfigConstants { public static readonly string DarkSkyApiSecret = Environment.GetEnvironmentVariable("DARK_SKY_TOKEN"); public static readonly string SlackApiSecret = Environment.GetEnvironmentVariable("SLACK_API_TOKEN"); - public static readonly string StanfordNlpFolder = Environment.GetEnvironmentVariable("STANFORD_NLP_FOLDER"); + public const string DbName = "SlackBotDb.db"; + public const string DarkSkyBaseUrl = "https://api.darksky.net"; } } diff --git a/SlackBotPrototype/API/DarkSkyWeatherProvider.cs b/SlackBotPrototype/API/DarkSkyWeatherProvider.cs index 879f9bb..33b484f 100644 --- a/SlackBotPrototype/API/DarkSkyWeatherProvider.cs +++ b/SlackBotPrototype/API/DarkSkyWeatherProvider.cs @@ -1,58 +1,44 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; using Newtonsoft.Json; -using RestSharp; namespace API { + public class DarkSkyWeatherProvider : IWeatherProvider { private readonly string _apiSecret; - private readonly RestClient _client = new RestClient("https://api.darksky.net"); + private readonly IHttpRequestClient _client; - public DarkSkyWeatherProvider(string apiSecret) + public DarkSkyWeatherProvider(string apiSecret, IHttpRequestClient client) { _apiSecret = apiSecret; + _client = client; } - public string GetForecast(DateTime when) + public string GetForecast(Location location, DateTime when) { - var forecastItem = GetForecastRaw(when); - return forecastItem.Daily.Data.FirstOrDefault()?.Summary; - } - - public string GetForecastWarning() - { - var today = GetForecastRaw(DateTime.Now); - var yesterday = GetForecastRaw(DateTime.Now.AddDays(-1)); - - // Darksky recommends using "icon" for automated purposes and "summary" for human readable output - return today.Daily.Data.FirstOrDefault()?.Icon != yesterday.Daily.Data.FirstOrDefault()?.Icon - ? today.Daily.Data.FirstOrDefault()?.Summary - : string.Empty; + var forecastItem = GetForecastRaw(location, when); + return forecastItem.Summary; } - public ForecastItem GetForecastRaw(DateTime when) + public ChatBotForecast GetForecastRaw(Location location, DateTime when) { + var timeStamp = when.DateTimeToUnixTimestamp(); - var resp = _client.Get(new RestRequest(Method.GET) - { - // To save time, hard code location - Resource = $"/forecast/{_apiSecret}/38.9072,-77.0369,{(int)timeStamp}?exclude=currently,minutely,hourly,flags" - }); - - if (resp.StatusCode != HttpStatusCode.OK) - { - return null; - } - - var content = resp.Content; + // SHORTCUT: We could cache requests in memory for recent forecast requests so we don't have + // to keep making network calls and improve performance. The tricky part would be + // figuring out what's the right invalidation scheme. It would probably involve the time + // returned in each request, location, and UTC time. Skipping it for now. + var content = _client.GetContent($"/forecast/{_apiSecret}/{location.Latitude},{location.Longitude},{(int)timeStamp}?exclude=currently,minutely,hourly,flags"); var forecastItem = JsonConvert.DeserializeObject(content); - return forecastItem; + + return new ChatBotForecast + { + Location = location, + Icon = forecastItem.Daily.Data.FirstOrDefault()?.Icon, + Summary = forecastItem.Daily.Data.FirstOrDefault()?.Summary + }; } } } diff --git a/SlackBotPrototype/API/Extensions.cs b/SlackBotPrototype/API/Extensions.cs index d7c5ddc..d409270 100644 --- a/SlackBotPrototype/API/Extensions.cs +++ b/SlackBotPrototype/API/Extensions.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace API { @@ -12,7 +8,7 @@ public static class Extensions public static double DateTimeToUnixTimestamp(this DateTime dateTime) { return (TimeZoneInfo.ConvertTimeToUtc(dateTime) - - new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc)).TotalSeconds; + new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; } } diff --git a/SlackBotPrototype/API/ExternalModels.cs b/SlackBotPrototype/API/ExternalModels.cs new file mode 100644 index 0000000..9005fe0 --- /dev/null +++ b/SlackBotPrototype/API/ExternalModels.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace API +{ + // Notes: Normally in web context, we'd have have separate domain + // models for back end and front end. In the case of the chatbot, we + // have model objects for external services and model objects for our our + // domain model (e.g. Users) + + public class ForecastItem + { + [JsonProperty("daily")] + public DailyForecastItem Daily { get; set; } + } + + public class DailyForecastItem + { + [JsonProperty("data")] + public List Data { get; set; } + } + + public class TemporalDataBlock + { + [JsonProperty("summary")] + public string Summary { get; set; } + [JsonProperty("icon")] + public string Icon { get; set; } + [JsonProperty("apparentTemperatureMax")] + public string ApparentTemperatureMax { get; set; } + } +} diff --git a/SlackBotPrototype/API/Interfaces.cs b/SlackBotPrototype/API/Interfaces.cs index d561529..2e46c6d 100644 --- a/SlackBotPrototype/API/Interfaces.cs +++ b/SlackBotPrototype/API/Interfaces.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace API { @@ -11,12 +8,58 @@ namespace API /// public interface IWeatherProvider { - string GetForecast(DateTime when); - string GetForecastWarning(); + string GetForecast(Location location, DateTime when); + ChatBotForecast GetForecastRaw(Location location, DateTime when); } - public interface IMessageParser + public interface IMessageToCommandConverter { - string HandleRequest(string message); + MessageParserResponse HandleRequest(string slackId, string message); } + + public interface IHttpRequestClient + { + /// + /// Makes an http GET request and returns the content. + /// + /// Normally we encapsulate the whole response object in a DTO object so we can handle different HTTP status + /// codes and error conditions. To save time, we're only going to handle HTTP Status = 200, other other status codes + /// will return no content. + /// + /// the url to resource to request + /// the content of the response if successful otherwise null + string GetContent(string url); + } + + /// + /// Define a interface for user persistence, in a production app the de-coupling + /// this allows us to swap ersistence strategies as needed from in-memory object, flat file, + /// or whatever DB desired + /// + public interface IRepository + { + User Get(string slackId); + Location GetLocation(string city, string state); + Location GetLocation(int id); + int CreateOrUpdate(string name, string slackId, Location location = null); + + // Shortcut: skipping some CRUD + } + + public interface ICommandImpl + { + // Invoke the command, expects a single string result. + // This could be changed later as needed to a more complex object. + string Invoke(); + } + + /// + /// Define an interface for a service that will provide location information + /// + public interface ILocationService + { + Location GetLocation(string city, string stateAbbreviation); + Location GetLocation(int id); + } + } diff --git a/SlackBotPrototype/API/LocationService.cs b/SlackBotPrototype/API/LocationService.cs new file mode 100644 index 0000000..c7e082d --- /dev/null +++ b/SlackBotPrototype/API/LocationService.cs @@ -0,0 +1,24 @@ +using System.Linq; + +namespace API +{ + public class LocationService : ILocationService + { + private readonly IRepository _repository; + + public LocationService(IRepository repository) + { + _repository = repository; + } + + public Location GetLocation(string city, string stateAbbreviation) + { + return _repository.GetLocation(city, stateAbbreviation); + } + + public Location GetLocation(int id) + { + return _repository.GetLocation(id); + } + } +} diff --git a/SlackBotPrototype/API/MessageToCommandConverter.cs b/SlackBotPrototype/API/MessageToCommandConverter.cs new file mode 100644 index 0000000..4a0a688 --- /dev/null +++ b/SlackBotPrototype/API/MessageToCommandConverter.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace API +{ + public class MessageToCommandConverter : IMessageToCommandConverter + { + private readonly IWeatherProvider _weatherProvider; + private readonly IRepository _repository; + + // Build a dictionary of regex patterns to match for commands, we iterate through sequentially so + // order of patterns matters, since the first match is the one we go with. Once we reach a certain number of commands this + // will become unwieldy. At that point we'd need to switch to something more robust for parsing like NLP + // + // Shortcut: We use a tuple here but this could be a class instead, reducing boilerplate by skipping another class + private readonly ICollection>> _messageMap = + new List>> + { + new Tuple>(new Regex("^<@.*>\\sset me to\\s(.*)\\s?[,]\\s(.*)$", RegexOptions.IgnoreCase), BuildSetLocation), + new Tuple>(new Regex("^<@.*>\\sweather\\s(now|today|tomorrow)$", RegexOptions.IgnoreCase), BuildWeatherNow), + new Tuple>(new Regex("^<@.*>\\sweather\\s(now|today|tomorrow)\\s(.*)\\s?[,]\\s(.*)$", RegexOptions.IgnoreCase), BuildWeatherNowWithLocation), + new Tuple>(new Regex("^<@.*>\\swhat should i wear", RegexOptions.IgnoreCase), BuildWhatToWear) + }; + + private readonly LocationService _locationService; + + private static ICommandImpl BuildSetLocation(SlackQueryDto dto) + { + var matches = dto.QueryParams; + var invalid = new InvalidCommand(); + + if (matches.Count == 0) + { + return invalid; + } + + var city = matches.Skip(0).Take(1).First(); + var state = matches.Skip(1).Take(1).First(); + + var location = dto.LocationService.GetLocation(city, state); + + return new SetUserLocationCommand(dto.Repository, dto.UserSlackId, dto.UserSlackId, location); + } + + private static ICommandImpl BuildWhatToWear(SlackQueryDto dto) + { + var weatherProvider = dto.WeatherProvider; + // Require user to set location first?? + return new WhatToWearCommand(weatherProvider); + } + + private static ICommandImpl BuildWeatherNow(SlackQueryDto dto) + { + var matches = dto.QueryParams; + var weatherProvider = dto.WeatherProvider; + + if (matches.Count == 0) + { + return new InvalidCommand("Timme for weather command not specified"); + } + + var timeParam = matches.First(); + + if (string.Equals(timeParam, "now", StringComparison.InvariantCultureIgnoreCase)) + { + return new WeatherNowCommand(weatherProvider, dto.Location, DateTime.Now); + } + + if (string.Equals(timeParam, "tomorrow", StringComparison.InvariantCultureIgnoreCase)) + { + return new WeatherNowCommand(weatherProvider, dto.Location, DateTime.Now.AddDays(1)); + } + + return new InvalidCommand("Expected `now` or `tomorrow`"); + } + + private static ICommandImpl BuildWeatherNowWithLocation(SlackQueryDto dto) + { + var matches = dto.QueryParams; + var weatherProvider = dto.WeatherProvider; + + if (matches.Count == 0) + { + return new InvalidCommand("Timme for weather command not specified"); + } + + var timeParam = matches.First(); + var city = matches.Skip(1).Take(1).First(); + var state = matches.Skip(2).Take(1).First(); + + + var location = dto.LocationService.GetLocation(city, state); + + if (location == null || location.IsInvalid()) + { + return new InvalidCommand($"Location not found for: {city} {state}"); + } + + if (string.Equals(timeParam, "now", StringComparison.InvariantCultureIgnoreCase)) + { + return new WeatherNowCommand(weatherProvider, location, DateTime.Now); + } + + if (string.Equals(timeParam, "tomorrow", StringComparison.InvariantCultureIgnoreCase)) + { + return new WeatherNowCommand(weatherProvider, location, DateTime.Now.AddDays(1)); + } + + return new InvalidCommand("Expected `now` or `tomorrow`"); + } + + public MessageToCommandConverter(IWeatherProvider weatherProvider, IRepository repository) + { + _weatherProvider = weatherProvider; + _repository = repository; + _locationService = new LocationService(_repository); + } + + /// + /// Parse a message and return a command that can be invoked + /// + /// the Slack Id of the user who queried bott + /// the message user sent + /// + public MessageParserResponse HandleRequest(string slackId, string message) + { + var noop = new MessageParserResponse + { + Command = new NoOpCommand(), + OriginalMessage = message + }; + + if (message == null) + { + return noop; + } + + var user = _repository.Get(slackId); + var location = _locationService.GetLocation(user?.LocationId ?? 0); + + foreach (var pair in _messageMap) + { + var match = pair.Item1.Match(message.Trim()); + + if (!match.Success) + { + continue; + } + + var capturedWords = new HashSet(); + + // first group is always the full original matched string so skip + for (var i = 1; i < match.Groups.Count; i++) + { + capturedWords.Add(match.Groups[i].Value); + } + + var queryDto = new SlackQueryDto + { + UserSlackId = slackId, + QueryParams = capturedWords, + // NOTE: We need to pass dependencies so static + // builders can use it. + WeatherProvider = _weatherProvider, + Repository = _repository, + LocationService = _locationService, + Location = location + }; + + // Shortcut: We could have each command handle the group matches + // themselves in order to handle more complex parameters + return new MessageParserResponse + { + Command = pair.Item2(queryDto), + OriginalMessage = message + }; + } + + return noop; + } + } + +} diff --git a/SlackBotPrototype/API/Models.cs b/SlackBotPrototype/API/Models.cs index e1707df..d31c62c 100644 --- a/SlackBotPrototype/API/Models.cs +++ b/SlackBotPrototype/API/Models.cs @@ -1,32 +1,80 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json; +using System.Collections.Generic; +using SQLite; namespace API { - - public class ForecastItem + public class User { - [JsonProperty("daily")] - public DailyForecastItem Daily { get; set; } + [PrimaryKey, AutoIncrement, Column("_id")] + public int Id { get; set; } + + [Indexed] + public string SlackId { get; set; } + public string Name { get; set; } + + // SHORTCUT: Reference the lookup in memory rather than using FK on actual DB table + public int? LocationId { get; set; } } - public class DailyForecastItem + // SHORTCUT: Skip storing this as actual entity in DB + public class Location { - [JsonProperty("data")] - public List Data { get; set; } + [PrimaryKey] + public int Id { get; set; } + [Indexed] + public string City { get; set; } + [Indexed] + public string State { get; set; } + [Indexed] + public string StateAbbreviation { get; set; } + public string DisplayName { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } + + public bool IsInvalid() + { + return string.IsNullOrWhiteSpace(DisplayName) || DisplayName == "Invalid"; + } } - public class TemporalDataBlock + public class ChatBotForecast { - [JsonProperty("summary")] public string Summary { get; set; } - [JsonProperty("icon")] public string Icon { get; set; } - [JsonProperty("apparentTemperatureMax")] - public string ApparentTemperatureMax { get; set; } + public Location Location { get; set; } } + + public enum BotCommands + { + Unknown, + WeatherNow, + WeatherTomorrow, + SetLocation, + WeatherLocation + } + + /// + /// Property bag for query context information that we need for building + /// commands. + /// + public class SlackQueryDto + { + // SHORTCUT: Instead of having to pass the weather provider around, + // we could use some sort of dependency injection here. + public IWeatherProvider WeatherProvider { get; set; } + public ILocationService LocationService { get; set; } + public IRepository Repository { get; set; } + public ICollection QueryParams { get; set; } + public string UserSlackId { get; set; } + public Location Location { get; set; } + } + + public class MessageParserResponse + { + + public ICommandImpl Command { get; set; } + + public string OriginalMessage { get; set; } + } + } diff --git a/SlackBotPrototype/API/Properties/AssemblyInfo.cs b/SlackBotPrototype/API/Properties/AssemblyInfo.cs index 0d0ac81..4d69960 100644 --- a/SlackBotPrototype/API/Properties/AssemblyInfo.cs +++ b/SlackBotPrototype/API/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/SlackBotPrototype/API/RestClientHttpProvider.cs b/SlackBotPrototype/API/RestClientHttpProvider.cs new file mode 100644 index 0000000..2526949 --- /dev/null +++ b/SlackBotPrototype/API/RestClientHttpProvider.cs @@ -0,0 +1,26 @@ +using System.Net; +using RestSharp; + +namespace API +{ + public class RestClientHttpProvider : IHttpRequestClient + { + private readonly RestClient _client; + + public RestClientHttpProvider(string baseUrl) + { + _client= new RestClient(baseUrl); + } + + public string GetContent(string url) + { + var resp = _client.Get(new RestRequest(Method.GET) + { + // To save time, hard code location + Resource = url + }); + + return resp.StatusCode == HttpStatusCode.OK ? resp.Content : null; + } + } +} diff --git a/SlackBotPrototype/API/SqLitePersistence.cs b/SlackBotPrototype/API/SqLitePersistence.cs new file mode 100644 index 0000000..4574cfa --- /dev/null +++ b/SlackBotPrototype/API/SqLitePersistence.cs @@ -0,0 +1,84 @@ +using System; +using System.Linq; +using SQLite; + +namespace API +{ + // SHORTCUT: Normally we'd break out the repositories into + // a file per entity. Keep them together for simplicity here. + // + // Also skipping tests for these because we'd need to set up integration tests + // with a SQLite database. If we weren't using a embedded file DB like SQLite, testing + // against a DB could get complicated. + public class SqLitePersistence : IRepository + { + + public User Get(string slackId) + { + if (string.IsNullOrWhiteSpace(slackId)) + { + return null; + } + + using (var db = new SQLiteConnection(ConfigConstants.DbName)) + { + return db.Query($"SELECT * FROM User WHERE SlackId = '{slackId}'").FirstOrDefault(); + } + } + + // SHORTCUT: We skip testing this because it's hard to test this in isolation, but it contains + // logic for matching up cities which is critical + public Location GetLocation(string city, string state) + { + if (string.IsNullOrWhiteSpace(city) || string.IsNullOrWhiteSpace(state)) + { + return null; + } + + using (var db = new SQLiteConnection(ConfigConstants.DbName)) + { + return db.Query($"SELECT * FROM Location WHERE UPPER(City) = '{city.ToUpper()}' AND UPPER(StateAbbreviation) = '{state.ToUpper()}'").FirstOrDefault(); + } + } + + public Location GetLocation(int id) + { + if (id <= 0) + { + return null; + } + + try + { + using (var db = new SQLiteConnection(ConfigConstants.DbName)) + { + return db.Get(id); + } + } + catch (Exception ex) + { + // SHORTCUT: Would normally log exception w/ logging library or service of choice + Console.WriteLine(ex.StackTrace); + } + + return null; + } + + public int CreateOrUpdate(string name, string slackId, Location location = null) + { + using (var db = new SQLiteConnection(ConfigConstants.DbName)) + { + // SHORTCUT: This should be wrapped in a transaction. Not doing so + // because I'm unfamiliar with this particular SQLite ORM + var id = db.InsertOrReplace(new User + { + Name = name, + SlackId = slackId, + LocationId = location?.Id + }); + + return id; + } + } + } +} diff --git a/SlackBotPrototype/API/packages.config b/SlackBotPrototype/API/packages.config index be39d9e..8c2c9cf 100644 --- a/SlackBotPrototype/API/packages.config +++ b/SlackBotPrototype/API/packages.config @@ -3,5 +3,12 @@ + + + + + + + \ No newline at end of file diff --git a/SlackBotPrototype/DBPreparer/DBPreparer.csproj b/SlackBotPrototype/DBPreparer/DBPreparer.csproj new file mode 100644 index 0000000..0fda789 --- /dev/null +++ b/SlackBotPrototype/DBPreparer/DBPreparer.csproj @@ -0,0 +1,93 @@ + + + + + Debug + AnyCPU + {41D05933-278A-45E7-A64D-933527E27883} + Exe + DBPreparer + DBPreparer + v4.5.2 + 512 + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + ..\packages\sqlite-net-pcl.1.5.151-beta\lib\netstandard1.1\SQLite-net.dll + + + ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll + + + ..\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll + + + ..\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll + + + ..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll + + + + + + + + + + + + + + + + + + Always + + + + + + {A234D7B8-EAB9-497F-84E7-354DC066BCB5} + API + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/SlackBotPrototype/DBPreparer/Program.cs b/SlackBotPrototype/DBPreparer/Program.cs new file mode 100644 index 0000000..b797992 --- /dev/null +++ b/SlackBotPrototype/DBPreparer/Program.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using API; +using Newtonsoft.Json; +using SQLite; + +namespace DBPreparer +{ + internal static class Program + { + static void Main(string[] args) + { + using (var db = new SQLiteConnection(ConfigConstants.DbName)) + { + // Prep DB schema initial state + db.CreateTable(); + db.CreateTable(); + + // SHORTCUT: Normally the source of the + // data would not be hard coded this way but rather come from a proper geocoding service, and we would store the results + // for performance. We would probably also prime the lookup table with the commmon locations that we anticipate so we + // don't have a cold start problem with locations. + var seedData = File.ReadAllText("locations-seed.json"); + var locations = JsonConvert.DeserializeObject(seedData); + + // Seed the database + db.InsertAll(locations); + + // Migration as needed as the application grows + } + } + } +} diff --git a/SlackBotPrototype/DBPreparer/Properties/AssemblyInfo.cs b/SlackBotPrototype/DBPreparer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4c97e73 --- /dev/null +++ b/SlackBotPrototype/DBPreparer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DBPreparer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DBPreparer")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("41d05933-278a-45e7-a64d-933527e27883")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SlackBotPrototype/DBPreparer/locations-seed.json b/SlackBotPrototype/DBPreparer/locations-seed.json new file mode 100644 index 0000000..d8aef86 --- /dev/null +++ b/SlackBotPrototype/DBPreparer/locations-seed.json @@ -0,0 +1,9002 @@ +[ + { + "Id": 1, + "City": "New York", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "New York, New York", + "Latitude": 40.7127837, + "Longitude": -74.0059413 + }, + { + "Id": 2, + "City": "Los Angeles", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Los Angeles, California", + "Latitude": 34.0522342, + "Longitude": -118.2436849 + }, + { + "Id": 3, + "City": "Chicago", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Chicago, Illinois", + "Latitude": 41.8781136, + "Longitude": -87.6297982 + }, + { + "Id": 4, + "City": "Houston", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Houston, Texas", + "Latitude": 29.7604267, + "Longitude": -95.3698028 + }, + { + "Id": 5, + "City": "Philadelphia", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Philadelphia, Pennsylvania", + "Latitude": 39.9525839, + "Longitude": -75.1652215 + }, + { + "Id": 6, + "City": "Phoenix", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Phoenix, Arizona", + "Latitude": 33.4483771, + "Longitude": -112.0740373 + }, + { + "Id": 7, + "City": "San Antonio", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "San Antonio, Texas", + "Latitude": 29.4241219, + "Longitude": -98.493628199999989 + }, + { + "Id": 8, + "City": "San Diego", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Diego, California", + "Latitude": 32.715738, + "Longitude": -117.1610838 + }, + { + "Id": 9, + "City": "Dallas", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Dallas, Texas", + "Latitude": 32.7766642, + "Longitude": -96.796987899999991 + }, + { + "Id": 10, + "City": "San Jose", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Jose, California", + "Latitude": 37.3382082, + "Longitude": -121.8863286 + }, + { + "Id": 11, + "City": "Austin", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Austin, Texas", + "Latitude": 30.267153, + "Longitude": -97.7430608 + }, + { + "Id": 12, + "City": "Indianapolis", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Indianapolis, Indiana", + "Latitude": 39.768403, + "Longitude": -86.158068 + }, + { + "Id": 13, + "City": "Jacksonville", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Jacksonville, Florida", + "Latitude": 30.3321838, + "Longitude": -81.655650999999992 + }, + { + "Id": 14, + "City": "San Francisco", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Francisco, California", + "Latitude": 37.7749295, + "Longitude": -122.4194155 + }, + { + "Id": 15, + "City": "Columbus", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Columbus, Ohio", + "Latitude": 39.9611755, + "Longitude": -82.998794199999992 + }, + { + "Id": 16, + "City": "Charlotte", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Charlotte, North Carolina", + "Latitude": 35.2270869, + "Longitude": -80.8431267 + }, + { + "Id": 17, + "City": "Fort Worth", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Fort Worth, Texas", + "Latitude": 32.7554883, + "Longitude": -97.3307658 + }, + { + "Id": 18, + "City": "Detroit", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Detroit, Michigan", + "Latitude": 42.331427, + "Longitude": -83.0457538 + }, + { + "Id": 19, + "City": "El Paso", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "El Paso, Texas", + "Latitude": 31.7775757, + "Longitude": -106.4424559 + }, + { + "Id": 20, + "City": "Memphis", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Memphis, Tennessee", + "Latitude": 35.1495343, + "Longitude": -90.0489801 + }, + { + "Id": 21, + "City": "Seattle", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Seattle, Washington", + "Latitude": 47.6062095, + "Longitude": -122.3320708 + }, + { + "Id": 22, + "City": "Denver", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Denver, Colorado", + "Latitude": 39.7392358, + "Longitude": -104.990251 + }, + { + "Id": 23, + "City": "Washington", + "State": "District of Columbia", + "StateAbbreviation": "DC", + "DisplayName": "Washington, District of Columbia", + "Latitude": 38.9071923, + "Longitude": -77.0368707 + }, + { + "Id": 24, + "City": "Boston", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Boston, Massachusetts", + "Latitude": 42.3600825, + "Longitude": -71.0588801 + }, + { + "Id": 25, + "City": "Nashville-Davidson", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Nashville-Davidson, Tennessee", + "Latitude": 36.1626638, + "Longitude": -86.7816016 + }, + { + "Id": 26, + "City": "Baltimore", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Baltimore, Maryland", + "Latitude": 39.2903848, + "Longitude": -76.6121893 + }, + { + "Id": 27, + "City": "Oklahoma City", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Oklahoma City, Oklahoma", + "Latitude": 35.4675602, + "Longitude": -97.5164276 + }, + { + "Id": 28, + "City": "Louisville/Jefferson County", + "State": "Kentucky", + "StateAbbreviation": "KY", + "DisplayName": "Louisville/Jefferson County, Kentucky", + "Latitude": 38.2526647, + "Longitude": -85.7584557 + }, + { + "Id": 29, + "City": "Portland", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Portland, Oregon", + "Latitude": 45.5230622, + "Longitude": -122.6764816 + }, + { + "Id": 30, + "City": "Las Vegas", + "State": "Nevada", + "StateAbbreviation": "NV", + "DisplayName": "Las Vegas, Nevada", + "Latitude": 36.1699412, + "Longitude": -115.1398296 + }, + { + "Id": 31, + "City": "Milwaukee", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Milwaukee, Wisconsin", + "Latitude": 43.0389025, + "Longitude": -87.9064736 + }, + { + "Id": 32, + "City": "Albuquerque", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Albuquerque, New Mexico", + "Latitude": 35.0853336, + "Longitude": -106.6055534 + }, + { + "Id": 33, + "City": "Tucson", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Tucson, Arizona", + "Latitude": 32.2217429, + "Longitude": -110.926479 + }, + { + "Id": 34, + "City": "Fresno", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Fresno, California", + "Latitude": 36.7468422, + "Longitude": -119.7725868 + }, + { + "Id": 35, + "City": "Sacramento", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Sacramento, California", + "Latitude": 38.5815719, + "Longitude": -121.4943996 + }, + { + "Id": 36, + "City": "Long Beach", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Long Beach, California", + "Latitude": 33.7700504, + "Longitude": -118.1937395 + }, + { + "Id": 37, + "City": "Kansas City", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Kansas City, Missouri", + "Latitude": 39.0997265, + "Longitude": -94.5785667 + }, + { + "Id": 38, + "City": "Mesa", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Mesa, Arizona", + "Latitude": 33.4151843, + "Longitude": -111.8314724 + }, + { + "Id": 39, + "City": "Virginia Beach", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Virginia Beach, Virginia", + "Latitude": 36.8529263, + "Longitude": -75.97798499999999 + }, + { + "Id": 40, + "City": "Atlanta", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Atlanta, Georgia", + "Latitude": 33.7489954, + "Longitude": -84.3879824 + }, + { + "Id": 41, + "City": "Colorado Springs", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Colorado Springs, Colorado", + "Latitude": 38.8338816, + "Longitude": -104.8213634 + }, + { + "Id": 42, + "City": "Omaha", + "State": "Nebraska", + "StateAbbreviation": "NE", + "DisplayName": "Omaha, Nebraska", + "Latitude": 41.2523634, + "Longitude": -95.997988299999989 + }, + { + "Id": 43, + "City": "Raleigh", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Raleigh, North Carolina", + "Latitude": 35.7795897, + "Longitude": -78.6381787 + }, + { + "Id": 44, + "City": "Miami", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Miami, Florida", + "Latitude": 25.7616798, + "Longitude": -80.1917902 + }, + { + "Id": 45, + "City": "Oakland", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Oakland, California", + "Latitude": 37.8043637, + "Longitude": -122.2711137 + }, + { + "Id": 46, + "City": "Minneapolis", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Minneapolis, Minnesota", + "Latitude": 44.977753, + "Longitude": -93.2650108 + }, + { + "Id": 47, + "City": "Tulsa", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Tulsa, Oklahoma", + "Latitude": 36.1539816, + "Longitude": -95.992775000000009 + }, + { + "Id": 48, + "City": "Cleveland", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Cleveland, Ohio", + "Latitude": 41.49932, + "Longitude": -81.6943605 + }, + { + "Id": 49, + "City": "Wichita", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Wichita, Kansas", + "Latitude": 37.688889, + "Longitude": -97.336111 + }, + { + "Id": 50, + "City": "Arlington", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Arlington, Texas", + "Latitude": 32.735687, + "Longitude": -97.108065599999989 + }, + { + "Id": 51, + "City": "New Orleans", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "New Orleans, Louisiana", + "Latitude": 29.951065799999991, + "Longitude": -90.0715323 + }, + { + "Id": 52, + "City": "Bakersfield", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Bakersfield, California", + "Latitude": 35.3732921, + "Longitude": -119.0187125 + }, + { + "Id": 53, + "City": "Tampa", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Tampa, Florida", + "Latitude": 27.950575, + "Longitude": -82.4571776 + }, + { + "Id": 54, + "City": "Honolulu", + "State": "Hawaii", + "StateAbbreviation": "HI", + "DisplayName": "Honolulu, Hawaii", + "Latitude": 21.3069444, + "Longitude": -157.8583333 + }, + { + "Id": 55, + "City": "Aurora", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Aurora, Colorado", + "Latitude": 39.7294319, + "Longitude": -104.8319195 + }, + { + "Id": 56, + "City": "Anaheim", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Anaheim, California", + "Latitude": 33.8352932, + "Longitude": -117.9145036 + }, + { + "Id": 57, + "City": "Santa Ana", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Ana, California", + "Latitude": 33.7455731, + "Longitude": -117.8678338 + }, + { + "Id": 58, + "City": "St. Louis", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "St. Louis, Missouri", + "Latitude": 38.6270025, + "Longitude": -90.199404199999989 + }, + { + "Id": 59, + "City": "Riverside", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Riverside, California", + "Latitude": 33.9533487, + "Longitude": -117.3961564 + }, + { + "Id": 60, + "City": "Corpus Christi", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Corpus Christi, Texas", + "Latitude": 27.8005828, + "Longitude": -97.396380999999991 + }, + { + "Id": 61, + "City": "Lexington-Fayette", + "State": "Kentucky", + "StateAbbreviation": "KY", + "DisplayName": "Lexington-Fayette, Kentucky", + "Latitude": 38.0405837, + "Longitude": -84.5037164 + }, + { + "Id": 62, + "City": "Pittsburgh", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Pittsburgh, Pennsylvania", + "Latitude": 40.440624799999988, + "Longitude": -79.9958864 + }, + { + "Id": 63, + "City": "Anchorage", + "State": "Alaska", + "StateAbbreviation": "AK", + "DisplayName": "Anchorage, Alaska", + "Latitude": 61.2180556, + "Longitude": -149.9002778 + }, + { + "Id": 64, + "City": "Stockton", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Stockton, California", + "Latitude": 37.9577016, + "Longitude": -121.2907796 + }, + { + "Id": 65, + "City": "Cincinnati", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Cincinnati, Ohio", + "Latitude": 39.1031182, + "Longitude": -84.5120196 + }, + { + "Id": 66, + "City": "St. Paul", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "St. Paul, Minnesota", + "Latitude": 44.9537029, + "Longitude": -93.0899578 + }, + { + "Id": 67, + "City": "Toledo", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Toledo, Ohio", + "Latitude": 41.6639383, + "Longitude": -83.555212000000012 + }, + { + "Id": 68, + "City": "Greensboro", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Greensboro, North Carolina", + "Latitude": 36.0726354, + "Longitude": -79.7919754 + }, + { + "Id": 69, + "City": "Newark", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Newark, New Jersey", + "Latitude": 40.735657, + "Longitude": -74.1723667 + }, + { + "Id": 70, + "City": "Plano", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Plano, Texas", + "Latitude": 33.0198431, + "Longitude": -96.6988856 + }, + { + "Id": 71, + "City": "Henderson", + "State": "Nevada", + "StateAbbreviation": "NV", + "DisplayName": "Henderson, Nevada", + "Latitude": 36.0395247, + "Longitude": -114.9817213 + }, + { + "Id": 72, + "City": "Lincoln", + "State": "Nebraska", + "StateAbbreviation": "NE", + "DisplayName": "Lincoln, Nebraska", + "Latitude": 40.8257625, + "Longitude": -96.6851982 + }, + { + "Id": 73, + "City": "Buffalo", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Buffalo, New York", + "Latitude": 42.886446799999987, + "Longitude": -78.8783689 + }, + { + "Id": 74, + "City": "Jersey City", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Jersey City, New Jersey", + "Latitude": 40.728157499999988, + "Longitude": -74.0776417 + }, + { + "Id": 75, + "City": "Chula Vista", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Chula Vista, California", + "Latitude": 32.6400541, + "Longitude": -117.0841955 + }, + { + "Id": 76, + "City": "Fort Wayne", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Fort Wayne, Indiana", + "Latitude": 41.079273, + "Longitude": -85.1393513 + }, + { + "Id": 77, + "City": "Orlando", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Orlando, Florida", + "Latitude": 28.5383355, + "Longitude": -81.3792365 + }, + { + "Id": 78, + "City": "St. Petersburg", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "St. Petersburg, Florida", + "Latitude": 27.773056, + "Longitude": -82.64 + }, + { + "Id": 79, + "City": "Chandler", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Chandler, Arizona", + "Latitude": 33.3061605, + "Longitude": -111.8412502 + }, + { + "Id": 80, + "City": "Laredo", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Laredo, Texas", + "Latitude": 27.5305671, + "Longitude": -99.48032409999999 + }, + { + "Id": 81, + "City": "Norfolk", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Norfolk, Virginia", + "Latitude": 36.8507689, + "Longitude": -76.28587259999999 + }, + { + "Id": 82, + "City": "Durham", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Durham, North Carolina", + "Latitude": 35.9940329, + "Longitude": -78.898619 + }, + { + "Id": 83, + "City": "Madison", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Madison, Wisconsin", + "Latitude": 43.0730517, + "Longitude": -89.4012302 + }, + { + "Id": 84, + "City": "Lubbock", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Lubbock, Texas", + "Latitude": 33.5778631, + "Longitude": -101.8551665 + }, + { + "Id": 85, + "City": "Irvine", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Irvine, California", + "Latitude": 33.6839473, + "Longitude": -117.7946942 + }, + { + "Id": 86, + "City": "Winston-Salem", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Winston-Salem, North Carolina", + "Latitude": 36.099859599999988, + "Longitude": -80.244216 + }, + { + "Id": 87, + "City": "Glendale", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Glendale, Arizona", + "Latitude": 33.5386523, + "Longitude": -112.1859866 + }, + { + "Id": 88, + "City": "Garland", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Garland, Texas", + "Latitude": 32.912624, + "Longitude": -96.638883299999989 + }, + { + "Id": 89, + "City": "Hialeah", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Hialeah, Florida", + "Latitude": 25.8575963, + "Longitude": -80.2781057 + }, + { + "Id": 90, + "City": "Reno", + "State": "Nevada", + "StateAbbreviation": "NV", + "DisplayName": "Reno, Nevada", + "Latitude": 39.5296329, + "Longitude": -119.8138027 + }, + { + "Id": 91, + "City": "Chesapeake", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Chesapeake, Virginia", + "Latitude": 36.7682088, + "Longitude": -76.2874927 + }, + { + "Id": 92, + "City": "Gilbert", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Gilbert, Arizona", + "Latitude": 33.3528264, + "Longitude": -111.789027 + }, + { + "Id": 93, + "City": "Baton Rouge", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Baton Rouge, Louisiana", + "Latitude": 30.4582829, + "Longitude": -91.1403196 + }, + { + "Id": 94, + "City": "Irving", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Irving, Texas", + "Latitude": 32.8140177, + "Longitude": -96.9488945 + }, + { + "Id": 95, + "City": "Scottsdale", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Scottsdale, Arizona", + "Latitude": 33.4941704, + "Longitude": -111.9260519 + }, + { + "Id": 96, + "City": "North Las Vegas", + "State": "Nevada", + "StateAbbreviation": "NV", + "DisplayName": "North Las Vegas, Nevada", + "Latitude": 36.1988592, + "Longitude": -115.1175013 + }, + { + "Id": 97, + "City": "Fremont", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Fremont, California", + "Latitude": 37.5482697, + "Longitude": -121.9885719 + }, + { + "Id": 98, + "City": "Boise City", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Boise City, Idaho", + "Latitude": 43.6187102, + "Longitude": -116.2146068 + }, + { + "Id": 99, + "City": "Richmond", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Richmond, Virginia", + "Latitude": 37.5407246, + "Longitude": -77.4360481 + }, + { + "Id": 100, + "City": "San Bernardino", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Bernardino, California", + "Latitude": 34.1083449, + "Longitude": -117.2897652 + }, + { + "Id": 101, + "City": "Birmingham", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Birmingham, Alabama", + "Latitude": 33.5206608, + "Longitude": -86.802489999999992 + }, + { + "Id": 102, + "City": "Spokane", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Spokane, Washington", + "Latitude": 47.6587802, + "Longitude": -117.4260466 + }, + { + "Id": 103, + "City": "Rochester", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Rochester, New York", + "Latitude": 43.16103, + "Longitude": -77.6109219 + }, + { + "Id": 104, + "City": "Des Moines", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Des Moines, Iowa", + "Latitude": 41.6005448, + "Longitude": -93.6091064 + }, + { + "Id": 105, + "City": "Modesto", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Modesto, California", + "Latitude": 37.639097199999988, + "Longitude": -120.9968782 + }, + { + "Id": 106, + "City": "Fayetteville", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Fayetteville, North Carolina", + "Latitude": 35.0526641, + "Longitude": -78.87835849999999 + }, + { + "Id": 107, + "City": "Tacoma", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Tacoma, Washington", + "Latitude": 47.2528768, + "Longitude": -122.4442906 + }, + { + "Id": 108, + "City": "Oxnard", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Oxnard, California", + "Latitude": 34.1975048, + "Longitude": -119.1770516 + }, + { + "Id": 109, + "City": "Fontana", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Fontana, California", + "Latitude": 34.0922335, + "Longitude": -117.435048 + }, + { + "Id": 110, + "City": "Columbus", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Columbus, Georgia", + "Latitude": 32.4609764, + "Longitude": -84.9877094 + }, + { + "Id": 111, + "City": "Montgomery", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Montgomery, Alabama", + "Latitude": 32.3668052, + "Longitude": -86.2999689 + }, + { + "Id": 112, + "City": "Moreno Valley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Moreno Valley, California", + "Latitude": 33.9424658, + "Longitude": -117.2296717 + }, + { + "Id": 113, + "City": "Shreveport", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Shreveport, Louisiana", + "Latitude": 32.5251516, + "Longitude": -93.7501789 + }, + { + "Id": 114, + "City": "Aurora", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Aurora, Illinois", + "Latitude": 41.7605849, + "Longitude": -88.320071500000012 + }, + { + "Id": 115, + "City": "Yonkers", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Yonkers, New York", + "Latitude": 40.9312099, + "Longitude": -73.898746899999992 + }, + { + "Id": 116, + "City": "Akron", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Akron, Ohio", + "Latitude": 41.0814447, + "Longitude": -81.519005299999989 + }, + { + "Id": 117, + "City": "Huntington Beach", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Huntington Beach, California", + "Latitude": 33.660297, + "Longitude": -117.9992265 + }, + { + "Id": 118, + "City": "Little Rock", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Little Rock, Arkansas", + "Latitude": 34.7464809, + "Longitude": -92.289594799999989 + }, + { + "Id": 119, + "City": "Augusta-Richmond County", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Augusta-Richmond County, Georgia", + "Latitude": 33.4734978, + "Longitude": -82.0105148 + }, + { + "Id": 120, + "City": "Amarillo", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Amarillo, Texas", + "Latitude": 35.2219971, + "Longitude": -101.8312969 + }, + { + "Id": 121, + "City": "Glendale", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Glendale, California", + "Latitude": 34.1425078, + "Longitude": -118.255075 + }, + { + "Id": 122, + "City": "Mobile", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Mobile, Alabama", + "Latitude": 30.6953657, + "Longitude": -88.0398912 + }, + { + "Id": 123, + "City": "Grand Rapids", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Grand Rapids, Michigan", + "Latitude": 42.9633599, + "Longitude": -85.6680863 + }, + { + "Id": 124, + "City": "Salt Lake City", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Salt Lake City, Utah", + "Latitude": 40.7607793, + "Longitude": -111.8910474 + }, + { + "Id": 125, + "City": "Tallahassee", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Tallahassee, Florida", + "Latitude": 30.4382559, + "Longitude": -84.28073289999999 + }, + { + "Id": 126, + "City": "Huntsville", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Huntsville, Alabama", + "Latitude": 34.7303688, + "Longitude": -86.5861037 + }, + { + "Id": 127, + "City": "Grand Prairie", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Grand Prairie, Texas", + "Latitude": 32.7459645, + "Longitude": -96.997784599999989 + }, + { + "Id": 128, + "City": "Knoxville", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Knoxville, Tennessee", + "Latitude": 35.9606384, + "Longitude": -83.9207392 + }, + { + "Id": 129, + "City": "Worcester", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Worcester, Massachusetts", + "Latitude": 42.2625932, + "Longitude": -71.8022934 + }, + { + "Id": 130, + "City": "Newport News", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Newport News, Virginia", + "Latitude": 37.0870821, + "Longitude": -76.4730122 + }, + { + "Id": 131, + "City": "Brownsville", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Brownsville, Texas", + "Latitude": 25.9017472, + "Longitude": -97.4974838 + }, + { + "Id": 132, + "City": "Overland Park", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Overland Park, Kansas", + "Latitude": 38.9822282, + "Longitude": -94.6707917 + }, + { + "Id": 133, + "City": "Santa Clarita", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Clarita, California", + "Latitude": 34.3916641, + "Longitude": -118.542586 + }, + { + "Id": 134, + "City": "Providence", + "State": "Rhode Island", + "StateAbbreviation": "RI", + "DisplayName": "Providence, Rhode Island", + "Latitude": 41.8239891, + "Longitude": -71.4128343 + }, + { + "Id": 135, + "City": "Garden Grove", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Garden Grove, California", + "Latitude": 33.7739053, + "Longitude": -117.9414477 + }, + { + "Id": 136, + "City": "Chattanooga", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Chattanooga, Tennessee", + "Latitude": 35.0456297, + "Longitude": -85.3096801 + }, + { + "Id": 137, + "City": "Oceanside", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Oceanside, California", + "Latitude": 33.1958696, + "Longitude": -117.3794834 + }, + { + "Id": 138, + "City": "Jackson", + "State": "Mississippi", + "StateAbbreviation": "MS", + "DisplayName": "Jackson, Mississippi", + "Latitude": 32.2987573, + "Longitude": -90.1848103 + }, + { + "Id": 139, + "City": "Fort Lauderdale", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Fort Lauderdale, Florida", + "Latitude": 26.1224386, + "Longitude": -80.137317400000015 + }, + { + "Id": 140, + "City": "Santa Rosa", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Rosa, California", + "Latitude": 38.440429, + "Longitude": -122.7140548 + }, + { + "Id": 141, + "City": "Rancho Cucamonga", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rancho Cucamonga, California", + "Latitude": 34.106398899999988, + "Longitude": -117.5931084 + }, + { + "Id": 142, + "City": "Port St. Lucie", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Port St. Lucie, Florida", + "Latitude": 27.2730492, + "Longitude": -80.3582261 + }, + { + "Id": 143, + "City": "Tempe", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Tempe, Arizona", + "Latitude": 33.4255104, + "Longitude": -111.9400054 + }, + { + "Id": 144, + "City": "Ontario", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Ontario, California", + "Latitude": 34.0633443, + "Longitude": -117.6508876 + }, + { + "Id": 145, + "City": "Vancouver", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Vancouver, Washington", + "Latitude": 45.6387281, + "Longitude": -122.6614861 + }, + { + "Id": 146, + "City": "Cape Coral", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Cape Coral, Florida", + "Latitude": 26.5628537, + "Longitude": -81.9495331 + }, + { + "Id": 147, + "City": "Sioux Falls", + "State": "South Dakota", + "StateAbbreviation": "SD", + "DisplayName": "Sioux Falls, South Dakota", + "Latitude": 43.5445959, + "Longitude": -96.731103400000009 + }, + { + "Id": 148, + "City": "Springfield", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Springfield, Missouri", + "Latitude": 37.2089572, + "Longitude": -93.292298899999992 + }, + { + "Id": 149, + "City": "Peoria", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Peoria, Arizona", + "Latitude": 33.5805955, + "Longitude": -112.2373779 + }, + { + "Id": 150, + "City": "Pembroke Pines", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Pembroke Pines, Florida", + "Latitude": 26.007765, + "Longitude": -80.2962555 + }, + { + "Id": 151, + "City": "Elk Grove", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Elk Grove, California", + "Latitude": 38.4087993, + "Longitude": -121.3716178 + }, + { + "Id": 152, + "City": "Salem", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Salem, Oregon", + "Latitude": 44.9428975, + "Longitude": -123.0350963 + }, + { + "Id": 153, + "City": "Lancaster", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lancaster, California", + "Latitude": 34.6867846, + "Longitude": -118.1541632 + }, + { + "Id": 154, + "City": "Corona", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Corona, California", + "Latitude": 33.8752935, + "Longitude": -117.5664384 + }, + { + "Id": 155, + "City": "Eugene", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Eugene, Oregon", + "Latitude": 44.0520691, + "Longitude": -123.0867536 + }, + { + "Id": 156, + "City": "Palmdale", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Palmdale, California", + "Latitude": 34.5794343, + "Longitude": -118.1164613 + }, + { + "Id": 157, + "City": "Salinas", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Salinas, California", + "Latitude": 36.6777372, + "Longitude": -121.6555013 + }, + { + "Id": 158, + "City": "Springfield", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Springfield, Massachusetts", + "Latitude": 42.1014831, + "Longitude": -72.589811 + }, + { + "Id": 159, + "City": "Pasadena", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Pasadena, Texas", + "Latitude": 29.6910625, + "Longitude": -95.2091006 + }, + { + "Id": 160, + "City": "Fort Collins", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Fort Collins, Colorado", + "Latitude": 40.5852602, + "Longitude": -105.084423 + }, + { + "Id": 161, + "City": "Hayward", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Hayward, California", + "Latitude": 37.6688205, + "Longitude": -122.0807964 + }, + { + "Id": 162, + "City": "Pomona", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Pomona, California", + "Latitude": 34.055103, + "Longitude": -117.7499909 + }, + { + "Id": 163, + "City": "Cary", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Cary, North Carolina", + "Latitude": 35.79154, + "Longitude": -78.7811169 + }, + { + "Id": 164, + "City": "Rockford", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Rockford, Illinois", + "Latitude": 42.2711311, + "Longitude": -89.0939952 + }, + { + "Id": 165, + "City": "Alexandria", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Alexandria, Virginia", + "Latitude": 38.8048355, + "Longitude": -77.0469214 + }, + { + "Id": 166, + "City": "Escondido", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Escondido, California", + "Latitude": 33.1192068, + "Longitude": -117.086421 + }, + { + "Id": 167, + "City": "McKinney", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "McKinney, Texas", + "Latitude": 33.1972465, + "Longitude": -96.6397822 + }, + { + "Id": 168, + "City": "Kansas City", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Kansas City, Kansas", + "Latitude": 39.114053, + "Longitude": -94.6274636 + }, + { + "Id": 169, + "City": "Joliet", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Joliet, Illinois", + "Latitude": 41.525031, + "Longitude": -88.0817251 + }, + { + "Id": 170, + "City": "Sunnyvale", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Sunnyvale, California", + "Latitude": 37.36883, + "Longitude": -122.0363496 + }, + { + "Id": 171, + "City": "Torrance", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Torrance, California", + "Latitude": 33.8358492, + "Longitude": -118.3406288 + }, + { + "Id": 172, + "City": "Bridgeport", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Bridgeport, Connecticut", + "Latitude": 41.1865478, + "Longitude": -73.19517669999999 + }, + { + "Id": 173, + "City": "Lakewood", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Lakewood, Colorado", + "Latitude": 39.7047095, + "Longitude": -105.0813734 + }, + { + "Id": 174, + "City": "Hollywood", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Hollywood, Florida", + "Latitude": 26.0112014, + "Longitude": -80.1494901 + }, + { + "Id": 175, + "City": "Paterson", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Paterson, New Jersey", + "Latitude": 40.9167654, + "Longitude": -74.171810999999991 + }, + { + "Id": 176, + "City": "Naperville", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Naperville, Illinois", + "Latitude": 41.7508391, + "Longitude": -88.1535352 + }, + { + "Id": 177, + "City": "Syracuse", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Syracuse, New York", + "Latitude": 43.0481221, + "Longitude": -76.147424399999991 + }, + { + "Id": 178, + "City": "Mesquite", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Mesquite, Texas", + "Latitude": 32.766795500000008, + "Longitude": -96.5991593 + }, + { + "Id": 179, + "City": "Dayton", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Dayton, Ohio", + "Latitude": 39.7589478, + "Longitude": -84.1916069 + }, + { + "Id": 180, + "City": "Savannah", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Savannah, Georgia", + "Latitude": 32.0835407, + "Longitude": -81.099834199999989 + }, + { + "Id": 181, + "City": "Clarksville", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Clarksville, Tennessee", + "Latitude": 36.5297706, + "Longitude": -87.3594528 + }, + { + "Id": 182, + "City": "Orange", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Orange, California", + "Latitude": 33.7877944, + "Longitude": -117.8531119 + }, + { + "Id": 183, + "City": "Pasadena", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Pasadena, California", + "Latitude": 34.1477849, + "Longitude": -118.1445155 + }, + { + "Id": 184, + "City": "Fullerton", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Fullerton, California", + "Latitude": 33.8703596, + "Longitude": -117.9242966 + }, + { + "Id": 185, + "City": "Killeen", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Killeen, Texas", + "Latitude": 31.1171194, + "Longitude": -97.72779589999999 + }, + { + "Id": 186, + "City": "Frisco", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Frisco, Texas", + "Latitude": 33.1506744, + "Longitude": -96.823611599999992 + }, + { + "Id": 187, + "City": "Hampton", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Hampton, Virginia", + "Latitude": 37.0298687, + "Longitude": -76.34522179999999 + }, + { + "Id": 188, + "City": "McAllen", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "McAllen, Texas", + "Latitude": 26.2034071, + "Longitude": -98.230012399999993 + }, + { + "Id": 189, + "City": "Warren", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Warren, Michigan", + "Latitude": 42.5144566, + "Longitude": -83.014652599999991 + }, + { + "Id": 190, + "City": "Bellevue", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Bellevue, Washington", + "Latitude": 47.610377, + "Longitude": -122.2006786 + }, + { + "Id": 191, + "City": "West Valley City", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "West Valley City, Utah", + "Latitude": 40.6916132, + "Longitude": -112.0010501 + }, + { + "Id": 192, + "City": "Columbia", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Columbia, South Carolina", + "Latitude": 34.0007104, + "Longitude": -81.0348144 + }, + { + "Id": 193, + "City": "Olathe", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Olathe, Kansas", + "Latitude": 38.8813958, + "Longitude": -94.819128499999991 + }, + { + "Id": 194, + "City": "Sterling Heights", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Sterling Heights, Michigan", + "Latitude": 42.5803122, + "Longitude": -83.0302033 + }, + { + "Id": 195, + "City": "New Haven", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "New Haven, Connecticut", + "Latitude": 41.308274, + "Longitude": -72.9278835 + }, + { + "Id": 196, + "City": "Miramar", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Miramar, Florida", + "Latitude": 25.9860762, + "Longitude": -80.303560199999993 + }, + { + "Id": 197, + "City": "Waco", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Waco, Texas", + "Latitude": 31.549333, + "Longitude": -97.1466695 + }, + { + "Id": 198, + "City": "Thousand Oaks", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Thousand Oaks, California", + "Latitude": 34.1705609, + "Longitude": -118.8375937 + }, + { + "Id": 199, + "City": "Cedar Rapids", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Cedar Rapids, Iowa", + "Latitude": 41.9778795, + "Longitude": -91.6656232 + }, + { + "Id": 200, + "City": "Charleston", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Charleston, South Carolina", + "Latitude": 32.7764749, + "Longitude": -79.931051200000013 + }, + { + "Id": 201, + "City": "Visalia", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Visalia, California", + "Latitude": 36.3302284, + "Longitude": -119.2920585 + }, + { + "Id": 202, + "City": "Topeka", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Topeka, Kansas", + "Latitude": 39.0558235, + "Longitude": -95.689018499999989 + }, + { + "Id": 203, + "City": "Elizabeth", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Elizabeth, New Jersey", + "Latitude": 40.6639916, + "Longitude": -74.2107006 + }, + { + "Id": 204, + "City": "Gainesville", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Gainesville, Florida", + "Latitude": 29.6516344, + "Longitude": -82.32482619999999 + }, + { + "Id": 205, + "City": "Thornton", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Thornton, Colorado", + "Latitude": 39.8680412, + "Longitude": -104.9719243 + }, + { + "Id": 206, + "City": "Roseville", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Roseville, California", + "Latitude": 38.7521235, + "Longitude": -121.2880059 + }, + { + "Id": 207, + "City": "Carrollton", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Carrollton, Texas", + "Latitude": 32.9756415, + "Longitude": -96.8899636 + }, + { + "Id": 208, + "City": "Coral Springs", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Coral Springs, Florida", + "Latitude": 26.271192, + "Longitude": -80.2706044 + }, + { + "Id": 209, + "City": "Stamford", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Stamford, Connecticut", + "Latitude": 41.0534302, + "Longitude": -73.5387341 + }, + { + "Id": 210, + "City": "Simi Valley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Simi Valley, California", + "Latitude": 34.2694474, + "Longitude": -118.781482 + }, + { + "Id": 211, + "City": "Concord", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Concord, California", + "Latitude": 37.9779776, + "Longitude": -122.0310733 + }, + { + "Id": 212, + "City": "Hartford", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Hartford, Connecticut", + "Latitude": 41.763711099999988, + "Longitude": -72.6850932 + }, + { + "Id": 213, + "City": "Kent", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Kent, Washington", + "Latitude": 47.3809335, + "Longitude": -122.2348431 + }, + { + "Id": 214, + "City": "Lafayette", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Lafayette, Louisiana", + "Latitude": 30.2240897, + "Longitude": -92.0198427 + }, + { + "Id": 215, + "City": "Midland", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Midland, Texas", + "Latitude": 31.9973456, + "Longitude": -102.0779146 + }, + { + "Id": 216, + "City": "Surprise", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Surprise, Arizona", + "Latitude": 33.6292337, + "Longitude": -112.3679279 + }, + { + "Id": 217, + "City": "Denton", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Denton, Texas", + "Latitude": 33.2148412, + "Longitude": -97.133068299999991 + }, + { + "Id": 218, + "City": "Victorville", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Victorville, California", + "Latitude": 34.5362184, + "Longitude": -117.2927641 + }, + { + "Id": 219, + "City": "Evansville", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Evansville, Indiana", + "Latitude": 37.9715592, + "Longitude": -87.5710898 + }, + { + "Id": 220, + "City": "Santa Clara", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Clara, California", + "Latitude": 37.3541079, + "Longitude": -121.9552356 + }, + { + "Id": 221, + "City": "Abilene", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Abilene, Texas", + "Latitude": 32.4487364, + "Longitude": -99.733143900000016 + }, + { + "Id": 222, + "City": "Athens-Clarke County", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Athens-Clarke County, Georgia", + "Latitude": 33.9519347, + "Longitude": -83.357567 + }, + { + "Id": 223, + "City": "Vallejo", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Vallejo, California", + "Latitude": 38.1040864, + "Longitude": -122.2566367 + }, + { + "Id": 224, + "City": "Allentown", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Allentown, Pennsylvania", + "Latitude": 40.6084305, + "Longitude": -75.4901833 + }, + { + "Id": 225, + "City": "Norman", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Norman, Oklahoma", + "Latitude": 35.2225668, + "Longitude": -97.4394777 + }, + { + "Id": 226, + "City": "Beaumont", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Beaumont, Texas", + "Latitude": 30.080174, + "Longitude": -94.1265562 + }, + { + "Id": 227, + "City": "Independence", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Independence, Missouri", + "Latitude": 39.0911161, + "Longitude": -94.415506799999989 + }, + { + "Id": 228, + "City": "Murfreesboro", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Murfreesboro, Tennessee", + "Latitude": 35.8456213, + "Longitude": -86.39027 + }, + { + "Id": 229, + "City": "Ann Arbor", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Ann Arbor, Michigan", + "Latitude": 42.2808256, + "Longitude": -83.7430378 + }, + { + "Id": 230, + "City": "Springfield", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Springfield, Illinois", + "Latitude": 39.781721300000008, + "Longitude": -89.6501481 + }, + { + "Id": 231, + "City": "Berkeley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Berkeley, California", + "Latitude": 37.8715926, + "Longitude": -122.272747 + }, + { + "Id": 232, + "City": "Peoria", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Peoria, Illinois", + "Latitude": 40.6936488, + "Longitude": -89.5889864 + }, + { + "Id": 233, + "City": "Provo", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Provo, Utah", + "Latitude": 40.2338438, + "Longitude": -111.6585337 + }, + { + "Id": 234, + "City": "El Monte", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "El Monte, California", + "Latitude": 34.0686206, + "Longitude": -118.0275667 + }, + { + "Id": 235, + "City": "Columbia", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Columbia, Missouri", + "Latitude": 38.9517053, + "Longitude": -92.3340724 + }, + { + "Id": 236, + "City": "Lansing", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Lansing, Michigan", + "Latitude": 42.732535, + "Longitude": -84.5555347 + }, + { + "Id": 237, + "City": "Fargo", + "State": "North Dakota", + "StateAbbreviation": "ND", + "DisplayName": "Fargo, North Dakota", + "Latitude": 46.8771863, + "Longitude": -96.7898034 + }, + { + "Id": 238, + "City": "Downey", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Downey, California", + "Latitude": 33.9401088, + "Longitude": -118.1331593 + }, + { + "Id": 239, + "City": "Costa Mesa", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Costa Mesa, California", + "Latitude": 33.6411316, + "Longitude": -117.9186689 + }, + { + "Id": 240, + "City": "Wilmington", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Wilmington, North Carolina", + "Latitude": 34.2257255, + "Longitude": -77.9447102 + }, + { + "Id": 241, + "City": "Arvada", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Arvada, Colorado", + "Latitude": 39.8027644, + "Longitude": -105.0874842 + }, + { + "Id": 242, + "City": "Inglewood", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Inglewood, California", + "Latitude": 33.9616801, + "Longitude": -118.3531311 + }, + { + "Id": 243, + "City": "Miami Gardens", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Miami Gardens, Florida", + "Latitude": 25.9420377, + "Longitude": -80.2456045 + }, + { + "Id": 244, + "City": "Carlsbad", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Carlsbad, California", + "Latitude": 33.1580933, + "Longitude": -117.3505939 + }, + { + "Id": 245, + "City": "Westminster", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Westminster, Colorado", + "Latitude": 39.8366528, + "Longitude": -105.0372046 + }, + { + "Id": 246, + "City": "Rochester", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Rochester, Minnesota", + "Latitude": 44.0121221, + "Longitude": -92.4801989 + }, + { + "Id": 247, + "City": "Odessa", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Odessa, Texas", + "Latitude": 31.8456816, + "Longitude": -102.3676431 + }, + { + "Id": 248, + "City": "Manchester", + "State": "New Hampshire", + "StateAbbreviation": "NH", + "DisplayName": "Manchester, New Hampshire", + "Latitude": 42.9956397, + "Longitude": -71.4547891 + }, + { + "Id": 249, + "City": "Elgin", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Elgin, Illinois", + "Latitude": 42.0354084, + "Longitude": -88.2825668 + }, + { + "Id": 250, + "City": "West Jordan", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "West Jordan, Utah", + "Latitude": 40.6096698, + "Longitude": -111.9391031 + }, + { + "Id": 251, + "City": "Round Rock", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Round Rock, Texas", + "Latitude": 30.5082551, + "Longitude": -97.678896 + }, + { + "Id": 252, + "City": "Clearwater", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Clearwater, Florida", + "Latitude": 27.9658533, + "Longitude": -82.8001026 + }, + { + "Id": 253, + "City": "Waterbury", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Waterbury, Connecticut", + "Latitude": 41.5581525, + "Longitude": -73.0514965 + }, + { + "Id": 254, + "City": "Gresham", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Gresham, Oregon", + "Latitude": 45.5001357, + "Longitude": -122.4302013 + }, + { + "Id": 255, + "City": "Fairfield", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Fairfield, California", + "Latitude": 38.249358099999988, + "Longitude": -122.0399663 + }, + { + "Id": 256, + "City": "Billings", + "State": "Montana", + "StateAbbreviation": "MT", + "DisplayName": "Billings, Montana", + "Latitude": 45.7832856, + "Longitude": -108.5006904 + }, + { + "Id": 257, + "City": "Lowell", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Lowell, Massachusetts", + "Latitude": 42.6334247, + "Longitude": -71.316171799999992 + }, + { + "Id": 258, + "City": "San Buenaventura (Ventura)", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Buenaventura (Ventura), California", + "Latitude": 34.274646, + "Longitude": -119.2290316 + }, + { + "Id": 259, + "City": "Pueblo", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Pueblo, Colorado", + "Latitude": 38.2544472, + "Longitude": -104.6091409 + }, + { + "Id": 260, + "City": "High Point", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "High Point, North Carolina", + "Latitude": 35.9556923, + "Longitude": -80.0053176 + }, + { + "Id": 261, + "City": "West Covina", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "West Covina, California", + "Latitude": 34.0686208, + "Longitude": -117.9389526 + }, + { + "Id": 262, + "City": "Richmond", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Richmond, California", + "Latitude": 37.9357576, + "Longitude": -122.3477486 + }, + { + "Id": 263, + "City": "Murrieta", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Murrieta, California", + "Latitude": 33.5539143, + "Longitude": -117.2139232 + }, + { + "Id": 264, + "City": "Cambridge", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Cambridge, Massachusetts", + "Latitude": 42.3736158, + "Longitude": -71.10973349999999 + }, + { + "Id": 265, + "City": "Antioch", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Antioch, California", + "Latitude": 38.0049214, + "Longitude": -121.805789 + }, + { + "Id": 266, + "City": "Temecula", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Temecula, California", + "Latitude": 33.4936391, + "Longitude": -117.1483648 + }, + { + "Id": 267, + "City": "Norwalk", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Norwalk, California", + "Latitude": 33.9022367, + "Longitude": -118.081733 + }, + { + "Id": 268, + "City": "Centennial", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Centennial, Colorado", + "Latitude": 39.5807452, + "Longitude": -104.8771726 + }, + { + "Id": 269, + "City": "Everett", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Everett, Washington", + "Latitude": 47.9789848, + "Longitude": -122.2020794 + }, + { + "Id": 270, + "City": "Palm Bay", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Palm Bay, Florida", + "Latitude": 28.0344621, + "Longitude": -80.5886646 + }, + { + "Id": 271, + "City": "Wichita Falls", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Wichita Falls, Texas", + "Latitude": 33.9137085, + "Longitude": -98.4933873 + }, + { + "Id": 272, + "City": "Green Bay", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Green Bay, Wisconsin", + "Latitude": 44.519158999999988, + "Longitude": -88.019826 + }, + { + "Id": 273, + "City": "Daly City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Daly City, California", + "Latitude": 37.6879241, + "Longitude": -122.4702079 + }, + { + "Id": 274, + "City": "Burbank", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Burbank, California", + "Latitude": 34.1808392, + "Longitude": -118.3089661 + }, + { + "Id": 275, + "City": "Richardson", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Richardson, Texas", + "Latitude": 32.9483335, + "Longitude": -96.7298519 + }, + { + "Id": 276, + "City": "Pompano Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Pompano Beach, Florida", + "Latitude": 26.2378597, + "Longitude": -80.1247667 + }, + { + "Id": 277, + "City": "North Charleston", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "North Charleston, South Carolina", + "Latitude": 32.8546197, + "Longitude": -79.9748103 + }, + { + "Id": 278, + "City": "Broken Arrow", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Broken Arrow, Oklahoma", + "Latitude": 36.060949, + "Longitude": -95.7974526 + }, + { + "Id": 279, + "City": "Boulder", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Boulder, Colorado", + "Latitude": 40.0149856, + "Longitude": -105.2705456 + }, + { + "Id": 280, + "City": "West Palm Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "West Palm Beach, Florida", + "Latitude": 26.7153424, + "Longitude": -80.0533746 + }, + { + "Id": 281, + "City": "Santa Maria", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Maria, California", + "Latitude": 34.9530337, + "Longitude": -120.4357191 + }, + { + "Id": 282, + "City": "El Cajon", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "El Cajon, California", + "Latitude": 32.7947731, + "Longitude": -116.9625269 + }, + { + "Id": 283, + "City": "Davenport", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Davenport, Iowa", + "Latitude": 41.5236437, + "Longitude": -90.5776367 + }, + { + "Id": 284, + "City": "Rialto", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rialto, California", + "Latitude": 34.1064001, + "Longitude": -117.3703235 + }, + { + "Id": 285, + "City": "Las Cruces", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Las Cruces, New Mexico", + "Latitude": 32.3199396, + "Longitude": -106.7636538 + }, + { + "Id": 286, + "City": "San Mateo", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Mateo, California", + "Latitude": 37.5629917, + "Longitude": -122.3255254 + }, + { + "Id": 287, + "City": "Lewisville", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Lewisville, Texas", + "Latitude": 33.046233, + "Longitude": -96.994174 + }, + { + "Id": 288, + "City": "South Bend", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "South Bend, Indiana", + "Latitude": 41.6763545, + "Longitude": -86.25198979999999 + }, + { + "Id": 289, + "City": "Lakeland", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Lakeland, Florida", + "Latitude": 28.0394654, + "Longitude": -81.9498042 + }, + { + "Id": 290, + "City": "Erie", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Erie, Pennsylvania", + "Latitude": 42.129224099999988, + "Longitude": -80.085059 + }, + { + "Id": 291, + "City": "Tyler", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Tyler, Texas", + "Latitude": 32.3512601, + "Longitude": -95.301062399999992 + }, + { + "Id": 292, + "City": "Pearland", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Pearland, Texas", + "Latitude": 29.5635666, + "Longitude": -95.2860474 + }, + { + "Id": 293, + "City": "College Station", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "College Station, Texas", + "Latitude": 30.627977, + "Longitude": -96.3344068 + }, + { + "Id": 294, + "City": "Kenosha", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Kenosha, Wisconsin", + "Latitude": 42.5847425, + "Longitude": -87.82118539999999 + }, + { + "Id": 295, + "City": "Sandy Springs", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Sandy Springs, Georgia", + "Latitude": 33.9304352, + "Longitude": -84.3733147 + }, + { + "Id": 296, + "City": "Clovis", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Clovis, California", + "Latitude": 36.8252277, + "Longitude": -119.7029194 + }, + { + "Id": 297, + "City": "Flint", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Flint, Michigan", + "Latitude": 43.0125274, + "Longitude": -83.6874562 + }, + { + "Id": 298, + "City": "Roanoke", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Roanoke, Virginia", + "Latitude": 37.2709704, + "Longitude": -79.9414266 + }, + { + "Id": 299, + "City": "Albany", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Albany, New York", + "Latitude": 42.6525793, + "Longitude": -73.7562317 + }, + { + "Id": 300, + "City": "Jurupa Valley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Jurupa Valley, California", + "Latitude": 33.9971974, + "Longitude": -117.4854802 + }, + { + "Id": 301, + "City": "Compton", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Compton, California", + "Latitude": 33.8958492, + "Longitude": -118.2200712 + }, + { + "Id": 302, + "City": "San Angelo", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "San Angelo, Texas", + "Latitude": 31.4637723, + "Longitude": -100.4370375 + }, + { + "Id": 303, + "City": "Hillsboro", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Hillsboro, Oregon", + "Latitude": 45.5228939, + "Longitude": -122.989827 + }, + { + "Id": 304, + "City": "Lawton", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Lawton, Oklahoma", + "Latitude": 34.6035669, + "Longitude": -98.395929099999989 + }, + { + "Id": 305, + "City": "Renton", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Renton, Washington", + "Latitude": 47.482877599999988, + "Longitude": -122.2170661 + }, + { + "Id": 306, + "City": "Vista", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Vista, California", + "Latitude": 33.2000368, + "Longitude": -117.2425355 + }, + { + "Id": 307, + "City": "Davie", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Davie, Florida", + "Latitude": 26.0764783, + "Longitude": -80.25211569999999 + }, + { + "Id": 308, + "City": "Greeley", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Greeley, Colorado", + "Latitude": 40.4233142, + "Longitude": -104.7091322 + }, + { + "Id": 309, + "City": "Mission Viejo", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Mission Viejo, California", + "Latitude": 33.6000232, + "Longitude": -117.6719953 + }, + { + "Id": 310, + "City": "Portsmouth", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Portsmouth, Virginia", + "Latitude": 36.8354258, + "Longitude": -76.2982742 + }, + { + "Id": 311, + "City": "Dearborn", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Dearborn, Michigan", + "Latitude": 42.3222599, + "Longitude": -83.176314499999989 + }, + { + "Id": 312, + "City": "South Gate", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "South Gate, California", + "Latitude": 33.954737, + "Longitude": -118.2120161 + }, + { + "Id": 313, + "City": "Tuscaloosa", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Tuscaloosa, Alabama", + "Latitude": 33.2098407, + "Longitude": -87.569173499999991 + }, + { + "Id": 314, + "City": "Livonia", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Livonia, Michigan", + "Latitude": 42.36837, + "Longitude": -83.352709699999991 + }, + { + "Id": 315, + "City": "New Bedford", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "New Bedford, Massachusetts", + "Latitude": 41.6362152, + "Longitude": -70.934204999999992 + }, + { + "Id": 316, + "City": "Vacaville", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Vacaville, California", + "Latitude": 38.3565773, + "Longitude": -121.9877444 + }, + { + "Id": 317, + "City": "Brockton", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Brockton, Massachusetts", + "Latitude": 42.0834335, + "Longitude": -71.0183787 + }, + { + "Id": 318, + "City": "Roswell", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Roswell, Georgia", + "Latitude": 34.0232431, + "Longitude": -84.3615555 + }, + { + "Id": 319, + "City": "Beaverton", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Beaverton, Oregon", + "Latitude": 45.487061999999987, + "Longitude": -122.8037102 + }, + { + "Id": 320, + "City": "Quincy", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Quincy, Massachusetts", + "Latitude": 42.2528772, + "Longitude": -71.0022705 + }, + { + "Id": 321, + "City": "Sparks", + "State": "Nevada", + "StateAbbreviation": "NV", + "DisplayName": "Sparks, Nevada", + "Latitude": 39.5349112, + "Longitude": -119.7526886 + }, + { + "Id": 322, + "City": "Yakima", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Yakima, Washington", + "Latitude": 46.6020711, + "Longitude": -120.5058987 + }, + { + "Id": 323, + "City": "Lee's Summit", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Lee's Summit, Missouri", + "Latitude": 38.9108408, + "Longitude": -94.3821724 + }, + { + "Id": 324, + "City": "Federal Way", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Federal Way, Washington", + "Latitude": 47.3223221, + "Longitude": -122.3126222 + }, + { + "Id": 325, + "City": "Carson", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Carson, California", + "Latitude": 33.8316745, + "Longitude": -118.281693 + }, + { + "Id": 326, + "City": "Santa Monica", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Monica, California", + "Latitude": 34.0194543, + "Longitude": -118.4911912 + }, + { + "Id": 327, + "City": "Hesperia", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Hesperia, California", + "Latitude": 34.4263886, + "Longitude": -117.3008784 + }, + { + "Id": 328, + "City": "Allen", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Allen, Texas", + "Latitude": 33.1031744, + "Longitude": -96.670550300000016 + }, + { + "Id": 329, + "City": "Rio Rancho", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Rio Rancho, New Mexico", + "Latitude": 35.2327544, + "Longitude": -106.6630437 + }, + { + "Id": 330, + "City": "Yuma", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Yuma, Arizona", + "Latitude": 32.6926512, + "Longitude": -114.6276916 + }, + { + "Id": 331, + "City": "Westminster", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Westminster, California", + "Latitude": 33.7513419, + "Longitude": -117.9939921 + }, + { + "Id": 332, + "City": "Orem", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Orem, Utah", + "Latitude": 40.2968979, + "Longitude": -111.6946475 + }, + { + "Id": 333, + "City": "Lynn", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Lynn, Massachusetts", + "Latitude": 42.466763000000007, + "Longitude": -70.9494938 + }, + { + "Id": 334, + "City": "Redding", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Redding, California", + "Latitude": 40.5865396, + "Longitude": -122.3916754 + }, + { + "Id": 335, + "City": "Spokane Valley", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Spokane Valley, Washington", + "Latitude": 47.6732281, + "Longitude": -117.2393748 + }, + { + "Id": 336, + "City": "Miami Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Miami Beach, Florida", + "Latitude": 25.790654, + "Longitude": -80.1300455 + }, + { + "Id": 337, + "City": "League City", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "League City, Texas", + "Latitude": 29.5074538, + "Longitude": -95.0949303 + }, + { + "Id": 338, + "City": "Lawrence", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Lawrence, Kansas", + "Latitude": 38.9716689, + "Longitude": -95.2352501 + }, + { + "Id": 339, + "City": "Santa Barbara", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Barbara, California", + "Latitude": 34.4208305, + "Longitude": -119.6981901 + }, + { + "Id": 340, + "City": "Plantation", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Plantation, Florida", + "Latitude": 26.1275862, + "Longitude": -80.233103599999993 + }, + { + "Id": 341, + "City": "Sandy", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Sandy, Utah", + "Latitude": 40.5649781, + "Longitude": -111.8389726 + }, + { + "Id": 342, + "City": "Sunrise", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Sunrise, Florida", + "Latitude": 26.1669711, + "Longitude": -80.25659499999999 + }, + { + "Id": 343, + "City": "Macon", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Macon, Georgia", + "Latitude": 32.8406946, + "Longitude": -83.6324022 + }, + { + "Id": 344, + "City": "Longmont", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Longmont, Colorado", + "Latitude": 40.1672068, + "Longitude": -105.1019275 + }, + { + "Id": 345, + "City": "Boca Raton", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Boca Raton, Florida", + "Latitude": 26.3683064, + "Longitude": -80.1289321 + }, + { + "Id": 346, + "City": "San Marcos", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Marcos, California", + "Latitude": 33.1433723, + "Longitude": -117.1661449 + }, + { + "Id": 347, + "City": "Greenville", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Greenville, North Carolina", + "Latitude": 35.612661, + "Longitude": -77.3663538 + }, + { + "Id": 348, + "City": "Waukegan", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Waukegan, Illinois", + "Latitude": 42.3636331, + "Longitude": -87.844793799999991 + }, + { + "Id": 349, + "City": "Fall River", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Fall River, Massachusetts", + "Latitude": 41.7014912, + "Longitude": -71.1550451 + }, + { + "Id": 350, + "City": "Chico", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Chico, California", + "Latitude": 39.7284944, + "Longitude": -121.8374777 + }, + { + "Id": 351, + "City": "Newton", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Newton, Massachusetts", + "Latitude": 42.3370413, + "Longitude": -71.20922139999999 + }, + { + "Id": 352, + "City": "San Leandro", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Leandro, California", + "Latitude": 37.7249296, + "Longitude": -122.1560768 + }, + { + "Id": 353, + "City": "Reading", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Reading, Pennsylvania", + "Latitude": 40.3356483, + "Longitude": -75.9268747 + }, + { + "Id": 354, + "City": "Norwalk", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Norwalk, Connecticut", + "Latitude": 41.117743999999988, + "Longitude": -73.4081575 + }, + { + "Id": 355, + "City": "Fort Smith", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Fort Smith, Arkansas", + "Latitude": 35.3859242, + "Longitude": -94.398547499999992 + }, + { + "Id": 356, + "City": "Newport Beach", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Newport Beach, California", + "Latitude": 33.6189101, + "Longitude": -117.9289469 + }, + { + "Id": 357, + "City": "Asheville", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Asheville, North Carolina", + "Latitude": 35.5950581, + "Longitude": -82.5514869 + }, + { + "Id": 358, + "City": "Nashua", + "State": "New Hampshire", + "StateAbbreviation": "NH", + "DisplayName": "Nashua, New Hampshire", + "Latitude": 42.7653662, + "Longitude": -71.467565999999991 + }, + { + "Id": 359, + "City": "Edmond", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Edmond, Oklahoma", + "Latitude": 35.6528323, + "Longitude": -97.478095400000015 + }, + { + "Id": 360, + "City": "Whittier", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Whittier, California", + "Latitude": 33.9791793, + "Longitude": -118.032844 + }, + { + "Id": 361, + "City": "Nampa", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Nampa, Idaho", + "Latitude": 43.5407172, + "Longitude": -116.5634624 + }, + { + "Id": 362, + "City": "Bloomington", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Bloomington, Minnesota", + "Latitude": 44.840798, + "Longitude": -93.2982799 + }, + { + "Id": 363, + "City": "Deltona", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Deltona, Florida", + "Latitude": 28.9005446, + "Longitude": -81.263673799999992 + }, + { + "Id": 364, + "City": "Hawthorne", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Hawthorne, California", + "Latitude": 33.9164032, + "Longitude": -118.3525748 + }, + { + "Id": 365, + "City": "Duluth", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Duluth, Minnesota", + "Latitude": 46.786671899999988, + "Longitude": -92.1004852 + }, + { + "Id": 366, + "City": "Carmel", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Carmel, Indiana", + "Latitude": 39.978371, + "Longitude": -86.1180435 + }, + { + "Id": 367, + "City": "Suffolk", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Suffolk, Virginia", + "Latitude": 36.7282054, + "Longitude": -76.5835621 + }, + { + "Id": 368, + "City": "Clifton", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Clifton, New Jersey", + "Latitude": 40.8584328, + "Longitude": -74.163755299999991 + }, + { + "Id": 369, + "City": "Citrus Heights", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Citrus Heights, California", + "Latitude": 38.7071247, + "Longitude": -121.2810611 + }, + { + "Id": 370, + "City": "Livermore", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Livermore, California", + "Latitude": 37.6818745, + "Longitude": -121.7680088 + }, + { + "Id": 371, + "City": "Tracy", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Tracy, California", + "Latitude": 37.7396513, + "Longitude": -121.4252227 + }, + { + "Id": 372, + "City": "Alhambra", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Alhambra, California", + "Latitude": 34.095287, + "Longitude": -118.1270146 + }, + { + "Id": 373, + "City": "Kirkland", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Kirkland, Washington", + "Latitude": 47.6814875, + "Longitude": -122.2087353 + }, + { + "Id": 374, + "City": "Trenton", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Trenton, New Jersey", + "Latitude": 40.2170534, + "Longitude": -74.7429384 + }, + { + "Id": 375, + "City": "Ogden", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Ogden, Utah", + "Latitude": 41.223, + "Longitude": -111.9738304 + }, + { + "Id": 376, + "City": "Hoover", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Hoover, Alabama", + "Latitude": 33.4053867, + "Longitude": -86.8113781 + }, + { + "Id": 377, + "City": "Cicero", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Cicero, Illinois", + "Latitude": 41.8455877, + "Longitude": -87.7539448 + }, + { + "Id": 378, + "City": "Fishers", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Fishers, Indiana", + "Latitude": 39.9567548, + "Longitude": -86.01335 + }, + { + "Id": 379, + "City": "Sugar Land", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Sugar Land, Texas", + "Latitude": 29.6196787, + "Longitude": -95.6349463 + }, + { + "Id": 380, + "City": "Danbury", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Danbury, Connecticut", + "Latitude": 41.394817, + "Longitude": -73.4540111 + }, + { + "Id": 381, + "City": "Meridian", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Meridian, Idaho", + "Latitude": 43.6121087, + "Longitude": -116.3915131 + }, + { + "Id": 382, + "City": "Indio", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Indio, California", + "Latitude": 33.7205771, + "Longitude": -116.2155619 + }, + { + "Id": 383, + "City": "Concord", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Concord, North Carolina", + "Latitude": 35.4087517, + "Longitude": -80.579511 + }, + { + "Id": 384, + "City": "Menifee", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Menifee, California", + "Latitude": 33.6971468, + "Longitude": -117.185294 + }, + { + "Id": 385, + "City": "Champaign", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Champaign, Illinois", + "Latitude": 40.1164204, + "Longitude": -88.2433829 + }, + { + "Id": 386, + "City": "Buena Park", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Buena Park, California", + "Latitude": 33.8675143, + "Longitude": -117.9981181 + }, + { + "Id": 387, + "City": "Troy", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Troy, Michigan", + "Latitude": 42.6064095, + "Longitude": -83.1497751 + }, + { + "Id": 388, + "City": "O'Fallon", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "O'Fallon, Missouri", + "Latitude": 38.8106075, + "Longitude": -90.699847699999992 + }, + { + "Id": 389, + "City": "Johns Creek", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Johns Creek, Georgia", + "Latitude": 34.0289259, + "Longitude": -84.198579 + }, + { + "Id": 390, + "City": "Bellingham", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Bellingham, Washington", + "Latitude": 48.74908, + "Longitude": -122.4781473 + }, + { + "Id": 391, + "City": "Westland", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Westland, Michigan", + "Latitude": 42.324203999999988, + "Longitude": -83.400211 + }, + { + "Id": 392, + "City": "Bloomington", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Bloomington, Indiana", + "Latitude": 39.165325, + "Longitude": -86.526385699999992 + }, + { + "Id": 393, + "City": "Sioux City", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Sioux City, Iowa", + "Latitude": 42.4999942, + "Longitude": -96.40030689999999 + }, + { + "Id": 394, + "City": "Warwick", + "State": "Rhode Island", + "StateAbbreviation": "RI", + "DisplayName": "Warwick, Rhode Island", + "Latitude": 41.7001009, + "Longitude": -71.4161671 + }, + { + "Id": 395, + "City": "Hemet", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Hemet, California", + "Latitude": 33.7475203, + "Longitude": -116.9719684 + }, + { + "Id": 396, + "City": "Longview", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Longview, Texas", + "Latitude": 32.5007037, + "Longitude": -94.740489099999991 + }, + { + "Id": 397, + "City": "Farmington Hills", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Farmington Hills, Michigan", + "Latitude": 42.4989936, + "Longitude": -83.3677168 + }, + { + "Id": 398, + "City": "Bend", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Bend, Oregon", + "Latitude": 44.0581728, + "Longitude": -121.3153096 + }, + { + "Id": 399, + "City": "Lakewood", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lakewood, California", + "Latitude": 33.8536269, + "Longitude": -118.1339563 + }, + { + "Id": 400, + "City": "Merced", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Merced, California", + "Latitude": 37.3021632, + "Longitude": -120.4829677 + }, + { + "Id": 401, + "City": "Mission", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Mission, Texas", + "Latitude": 26.2159066, + "Longitude": -98.32529319999999 + }, + { + "Id": 402, + "City": "Chino", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Chino, California", + "Latitude": 34.0122346, + "Longitude": -117.688944 + }, + { + "Id": 403, + "City": "Redwood City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Redwood City, California", + "Latitude": 37.485215200000013, + "Longitude": -122.2363548 + }, + { + "Id": 404, + "City": "Edinburg", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Edinburg, Texas", + "Latitude": 26.3017374, + "Longitude": -98.1633432 + }, + { + "Id": 405, + "City": "Cranston", + "State": "Rhode Island", + "StateAbbreviation": "RI", + "DisplayName": "Cranston, Rhode Island", + "Latitude": 41.7798226, + "Longitude": -71.4372796 + }, + { + "Id": 406, + "City": "Parma", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Parma, Ohio", + "Latitude": 41.4047742, + "Longitude": -81.7229086 + }, + { + "Id": 407, + "City": "New Rochelle", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "New Rochelle, New York", + "Latitude": 40.9114882, + "Longitude": -73.7823549 + }, + { + "Id": 408, + "City": "Lake Forest", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lake Forest, California", + "Latitude": 33.6469661, + "Longitude": -117.689218 + }, + { + "Id": 409, + "City": "Napa", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Napa, California", + "Latitude": 38.2975381, + "Longitude": -122.286865 + }, + { + "Id": 410, + "City": "Hammond", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Hammond, Indiana", + "Latitude": 41.5833688, + "Longitude": -87.5000412 + }, + { + "Id": 411, + "City": "Fayetteville", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Fayetteville, Arkansas", + "Latitude": 36.0625795, + "Longitude": -94.1574263 + }, + { + "Id": 412, + "City": "Bloomington", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Bloomington, Illinois", + "Latitude": 40.4842027, + "Longitude": -88.993687299999991 + }, + { + "Id": 413, + "City": "Avondale", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Avondale, Arizona", + "Latitude": 33.4355977, + "Longitude": -112.3496021 + }, + { + "Id": 414, + "City": "Somerville", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Somerville, Massachusetts", + "Latitude": 42.3875968, + "Longitude": -71.0994968 + }, + { + "Id": 415, + "City": "Palm Coast", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Palm Coast, Florida", + "Latitude": 29.5844524, + "Longitude": -81.207869899999992 + }, + { + "Id": 416, + "City": "Bryan", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Bryan, Texas", + "Latitude": 30.6743643, + "Longitude": -96.3699632 + }, + { + "Id": 417, + "City": "Gary", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Gary, Indiana", + "Latitude": 41.5933696, + "Longitude": -87.3464271 + }, + { + "Id": 418, + "City": "Largo", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Largo, Florida", + "Latitude": 27.9094665, + "Longitude": -82.7873244 + }, + { + "Id": 419, + "City": "Brooklyn Park", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Brooklyn Park, Minnesota", + "Latitude": 45.0941315, + "Longitude": -93.3563405 + }, + { + "Id": 420, + "City": "Tustin", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Tustin, California", + "Latitude": 33.7458511, + "Longitude": -117.826166 + }, + { + "Id": 421, + "City": "Racine", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Racine, Wisconsin", + "Latitude": 42.7261309, + "Longitude": -87.782852300000016 + }, + { + "Id": 422, + "City": "Deerfield Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Deerfield Beach, Florida", + "Latitude": 26.3184123, + "Longitude": -80.099765699999992 + }, + { + "Id": 423, + "City": "Lynchburg", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Lynchburg, Virginia", + "Latitude": 37.4137536, + "Longitude": -79.142246399999991 + }, + { + "Id": 424, + "City": "Mountain View", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Mountain View, California", + "Latitude": 37.3860517, + "Longitude": -122.0838511 + }, + { + "Id": 425, + "City": "Medford", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Medford, Oregon", + "Latitude": 42.3265152, + "Longitude": -122.8755949 + }, + { + "Id": 426, + "City": "Lawrence", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Lawrence, Massachusetts", + "Latitude": 42.7070354, + "Longitude": -71.1631137 + }, + { + "Id": 427, + "City": "Bellflower", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Bellflower, California", + "Latitude": 33.8816818, + "Longitude": -118.1170117 + }, + { + "Id": 428, + "City": "Melbourne", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Melbourne, Florida", + "Latitude": 28.0836269, + "Longitude": -80.608108899999991 + }, + { + "Id": 429, + "City": "St. Joseph", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "St. Joseph, Missouri", + "Latitude": 39.7674578, + "Longitude": -94.84668099999999 + }, + { + "Id": 430, + "City": "Camden", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Camden, New Jersey", + "Latitude": 39.9259463, + "Longitude": -75.1196199 + }, + { + "Id": 431, + "City": "St. George", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "St. George, Utah", + "Latitude": 37.0965278, + "Longitude": -113.5684164 + }, + { + "Id": 432, + "City": "Kennewick", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Kennewick, Washington", + "Latitude": 46.2112458, + "Longitude": -119.1372338 + }, + { + "Id": 433, + "City": "Baldwin Park", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Baldwin Park, California", + "Latitude": 34.0852868, + "Longitude": -117.9608978 + }, + { + "Id": 434, + "City": "Chino Hills", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Chino Hills, California", + "Latitude": 33.9898188, + "Longitude": -117.7325848 + }, + { + "Id": 435, + "City": "Alameda", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Alameda, California", + "Latitude": 37.7652065, + "Longitude": -122.2416355 + }, + { + "Id": 436, + "City": "Albany", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Albany, Georgia", + "Latitude": 31.5785074, + "Longitude": -84.155740999999992 + }, + { + "Id": 437, + "City": "Arlington Heights", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Arlington Heights, Illinois", + "Latitude": 42.0883603, + "Longitude": -87.980626500000014 + }, + { + "Id": 438, + "City": "Scranton", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Scranton, Pennsylvania", + "Latitude": 41.408969, + "Longitude": -75.662412199999991 + }, + { + "Id": 439, + "City": "Evanston", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Evanston, Illinois", + "Latitude": 42.0450722, + "Longitude": -87.687696899999992 + }, + { + "Id": 440, + "City": "Kalamazoo", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Kalamazoo, Michigan", + "Latitude": 42.2917069, + "Longitude": -85.5872286 + }, + { + "Id": 441, + "City": "Baytown", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Baytown, Texas", + "Latitude": 29.7355047, + "Longitude": -94.97742740000001 + }, + { + "Id": 442, + "City": "Upland", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Upland, California", + "Latitude": 34.09751, + "Longitude": -117.6483876 + }, + { + "Id": 443, + "City": "Springdale", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Springdale, Arkansas", + "Latitude": 36.186744200000007, + "Longitude": -94.1288141 + }, + { + "Id": 444, + "City": "Bethlehem", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Bethlehem, Pennsylvania", + "Latitude": 40.6259316, + "Longitude": -75.370457899999991 + }, + { + "Id": 445, + "City": "Schaumburg", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Schaumburg, Illinois", + "Latitude": 42.0333607, + "Longitude": -88.0834059 + }, + { + "Id": 446, + "City": "Mount Pleasant", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Mount Pleasant, South Carolina", + "Latitude": 32.8323225, + "Longitude": -79.828425799999991 + }, + { + "Id": 447, + "City": "Auburn", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Auburn, Washington", + "Latitude": 47.307322799999987, + "Longitude": -122.2284532 + }, + { + "Id": 448, + "City": "Decatur", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Decatur, Illinois", + "Latitude": 39.8403147, + "Longitude": -88.9548001 + }, + { + "Id": 449, + "City": "San Ramon", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Ramon, California", + "Latitude": 37.7799273, + "Longitude": -121.9780153 + }, + { + "Id": 450, + "City": "Pleasanton", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Pleasanton, California", + "Latitude": 37.6624312, + "Longitude": -121.8746789 + }, + { + "Id": 451, + "City": "Wyoming", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Wyoming, Michigan", + "Latitude": 42.9133602, + "Longitude": -85.7053085 + }, + { + "Id": 452, + "City": "Lake Charles", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Lake Charles, Louisiana", + "Latitude": 30.2265949, + "Longitude": -93.2173758 + }, + { + "Id": 453, + "City": "Plymouth", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Plymouth, Minnesota", + "Latitude": 45.0105194, + "Longitude": -93.4555093 + }, + { + "Id": 454, + "City": "Bolingbrook", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Bolingbrook, Illinois", + "Latitude": 41.698641599999988, + "Longitude": -88.0683955 + }, + { + "Id": 455, + "City": "Pharr", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Pharr, Texas", + "Latitude": 26.1947962, + "Longitude": -98.1836216 + }, + { + "Id": 456, + "City": "Appleton", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Appleton, Wisconsin", + "Latitude": 44.2619309, + "Longitude": -88.41538469999999 + }, + { + "Id": 457, + "City": "Gastonia", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Gastonia, North Carolina", + "Latitude": 35.262082, + "Longitude": -81.187300499999992 + }, + { + "Id": 458, + "City": "Folsom", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Folsom, California", + "Latitude": 38.6779591, + "Longitude": -121.1760583 + }, + { + "Id": 459, + "City": "Southfield", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Southfield, Michigan", + "Latitude": 42.4733688, + "Longitude": -83.2218731 + }, + { + "Id": 460, + "City": "Rochester Hills", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Rochester Hills, Michigan", + "Latitude": 42.658366099999988, + "Longitude": -83.1499322 + }, + { + "Id": 461, + "City": "New Britain", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "New Britain, Connecticut", + "Latitude": 41.6612104, + "Longitude": -72.7795419 + }, + { + "Id": 462, + "City": "Goodyear", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Goodyear, Arizona", + "Latitude": 33.4353394, + "Longitude": -112.3576567 + }, + { + "Id": 463, + "City": "Canton", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Canton, Ohio", + "Latitude": 40.798947299999988, + "Longitude": -81.378447 + }, + { + "Id": 464, + "City": "Warner Robins", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Warner Robins, Georgia", + "Latitude": 32.6130007, + "Longitude": -83.624201 + }, + { + "Id": 465, + "City": "Union City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Union City, California", + "Latitude": 37.5933918, + "Longitude": -122.0438298 + }, + { + "Id": 466, + "City": "Perris", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Perris, California", + "Latitude": 33.7825194, + "Longitude": -117.2286478 + }, + { + "Id": 467, + "City": "Manteca", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Manteca, California", + "Latitude": 37.7974273, + "Longitude": -121.2160526 + }, + { + "Id": 468, + "City": "Iowa City", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Iowa City, Iowa", + "Latitude": 41.6611277, + "Longitude": -91.5301683 + }, + { + "Id": 469, + "City": "Jonesboro", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Jonesboro, Arkansas", + "Latitude": 35.842296700000013, + "Longitude": -90.704279 + }, + { + "Id": 470, + "City": "Wilmington", + "State": "Delaware", + "StateAbbreviation": "DE", + "DisplayName": "Wilmington, Delaware", + "Latitude": 39.7390721, + "Longitude": -75.5397878 + }, + { + "Id": 471, + "City": "Lynwood", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lynwood, California", + "Latitude": 33.930293, + "Longitude": -118.2114603 + }, + { + "Id": 472, + "City": "Loveland", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Loveland, Colorado", + "Latitude": 40.3977612, + "Longitude": -105.0749801 + }, + { + "Id": 473, + "City": "Pawtucket", + "State": "Rhode Island", + "StateAbbreviation": "RI", + "DisplayName": "Pawtucket, Rhode Island", + "Latitude": 41.878711, + "Longitude": -71.382555799999992 + }, + { + "Id": 474, + "City": "Boynton Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Boynton Beach, Florida", + "Latitude": 26.5317866, + "Longitude": -80.0905465 + }, + { + "Id": 475, + "City": "Waukesha", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Waukesha, Wisconsin", + "Latitude": 43.0116784, + "Longitude": -88.2314813 + }, + { + "Id": 476, + "City": "Gulfport", + "State": "Mississippi", + "StateAbbreviation": "MS", + "DisplayName": "Gulfport, Mississippi", + "Latitude": 30.3674198, + "Longitude": -89.0928155 + }, + { + "Id": 477, + "City": "Apple Valley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Apple Valley, California", + "Latitude": 34.5008311, + "Longitude": -117.1858759 + }, + { + "Id": 478, + "City": "Passaic", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Passaic, New Jersey", + "Latitude": 40.8567662, + "Longitude": -74.1284764 + }, + { + "Id": 479, + "City": "Rapid City", + "State": "South Dakota", + "StateAbbreviation": "SD", + "DisplayName": "Rapid City, South Dakota", + "Latitude": 44.0805434, + "Longitude": -103.2310149 + }, + { + "Id": 480, + "City": "Layton", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Layton, Utah", + "Latitude": 41.0602216, + "Longitude": -111.9710529 + }, + { + "Id": 481, + "City": "Lafayette", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Lafayette, Indiana", + "Latitude": 40.4167022, + "Longitude": -86.875286899999992 + }, + { + "Id": 482, + "City": "Turlock", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Turlock, California", + "Latitude": 37.4946568, + "Longitude": -120.8465941 + }, + { + "Id": 483, + "City": "Muncie", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Muncie, Indiana", + "Latitude": 40.1933767, + "Longitude": -85.3863599 + }, + { + "Id": 484, + "City": "Temple", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Temple, Texas", + "Latitude": 31.0982344, + "Longitude": -97.342782 + }, + { + "Id": 485, + "City": "Missouri City", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Missouri City, Texas", + "Latitude": 29.6185669, + "Longitude": -95.5377215 + }, + { + "Id": 486, + "City": "Redlands", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Redlands, California", + "Latitude": 34.0555693, + "Longitude": -117.1825381 + }, + { + "Id": 487, + "City": "Santa Fe", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Santa Fe, New Mexico", + "Latitude": 35.6869752, + "Longitude": -105.937799 + }, + { + "Id": 488, + "City": "Lauderhill", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Lauderhill, Florida", + "Latitude": 26.1403635, + "Longitude": -80.2133808 + }, + { + "Id": 489, + "City": "Milpitas", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Milpitas, California", + "Latitude": 37.4323341, + "Longitude": -121.8995741 + }, + { + "Id": 490, + "City": "Palatine", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Palatine, Illinois", + "Latitude": 42.1103041, + "Longitude": -88.034240000000011 + }, + { + "Id": 491, + "City": "Missoula", + "State": "Montana", + "StateAbbreviation": "MT", + "DisplayName": "Missoula, Montana", + "Latitude": 46.878717599999987, + "Longitude": -113.996586 + }, + { + "Id": 492, + "City": "Rock Hill", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Rock Hill, South Carolina", + "Latitude": 34.9248667, + "Longitude": -81.025078400000012 + }, + { + "Id": 493, + "City": "Jacksonville", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Jacksonville, North Carolina", + "Latitude": 34.7540524, + "Longitude": -77.4302414 + }, + { + "Id": 494, + "City": "Franklin", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Franklin, Tennessee", + "Latitude": 35.9250637, + "Longitude": -86.8688899 + }, + { + "Id": 495, + "City": "Flagstaff", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Flagstaff, Arizona", + "Latitude": 35.1982836, + "Longitude": -111.651302 + }, + { + "Id": 496, + "City": "Flower Mound", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Flower Mound, Texas", + "Latitude": 33.0145673, + "Longitude": -97.0969552 + }, + { + "Id": 497, + "City": "Weston", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Weston, Florida", + "Latitude": 26.1003654, + "Longitude": -80.3997748 + }, + { + "Id": 498, + "City": "Waterloo", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Waterloo, Iowa", + "Latitude": 42.492786, + "Longitude": -92.34257749999999 + }, + { + "Id": 499, + "City": "Union City", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Union City, New Jersey", + "Latitude": 40.6975898, + "Longitude": -74.26316349999999 + }, + { + "Id": 500, + "City": "Mount Vernon", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Mount Vernon, New York", + "Latitude": 40.9125992, + "Longitude": -73.8370786 + }, + { + "Id": 501, + "City": "Fort Myers", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Fort Myers, Florida", + "Latitude": 26.640628, + "Longitude": -81.8723084 + }, + { + "Id": 502, + "City": "Dothan", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Dothan, Alabama", + "Latitude": 31.2232313, + "Longitude": -85.3904888 + }, + { + "Id": 503, + "City": "Rancho Cordova", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rancho Cordova, California", + "Latitude": 38.5890723, + "Longitude": -121.302728 + }, + { + "Id": 504, + "City": "Redondo Beach", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Redondo Beach, California", + "Latitude": 33.8491816, + "Longitude": -118.3884078 + }, + { + "Id": 505, + "City": "Jackson", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Jackson, Tennessee", + "Latitude": 35.6145169, + "Longitude": -88.813946899999991 + }, + { + "Id": 506, + "City": "Pasco", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Pasco, Washington", + "Latitude": 46.2395793, + "Longitude": -119.1005657 + }, + { + "Id": 507, + "City": "St. Charles", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "St. Charles, Missouri", + "Latitude": 38.7881062, + "Longitude": -90.4974359 + }, + { + "Id": 508, + "City": "Eau Claire", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Eau Claire, Wisconsin", + "Latitude": 44.811349, + "Longitude": -91.4984941 + }, + { + "Id": 509, + "City": "North Richland Hills", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "North Richland Hills, Texas", + "Latitude": 32.8342952, + "Longitude": -97.2289029 + }, + { + "Id": 510, + "City": "Bismarck", + "State": "North Dakota", + "StateAbbreviation": "ND", + "DisplayName": "Bismarck, North Dakota", + "Latitude": 46.8083268, + "Longitude": -100.7837392 + }, + { + "Id": 511, + "City": "Yorba Linda", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Yorba Linda, California", + "Latitude": 33.8886259, + "Longitude": -117.8131125 + }, + { + "Id": 512, + "City": "Kenner", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Kenner, Louisiana", + "Latitude": 29.9940924, + "Longitude": -90.2417434 + }, + { + "Id": 513, + "City": "Walnut Creek", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Walnut Creek, California", + "Latitude": 37.9100783, + "Longitude": -122.0651819 + }, + { + "Id": 514, + "City": "Frederick", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Frederick, Maryland", + "Latitude": 39.414268799999988, + "Longitude": -77.4105409 + }, + { + "Id": 515, + "City": "Oshkosh", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Oshkosh, Wisconsin", + "Latitude": 44.0247062, + "Longitude": -88.5426136 + }, + { + "Id": 516, + "City": "Pittsburg", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Pittsburg, California", + "Latitude": 38.0279762, + "Longitude": -121.8846806 + }, + { + "Id": 517, + "City": "Palo Alto", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Palo Alto, California", + "Latitude": 37.4418834, + "Longitude": -122.1430195 + }, + { + "Id": 518, + "City": "Bossier City", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Bossier City, Louisiana", + "Latitude": 32.5159852, + "Longitude": -93.7321228 + }, + { + "Id": 519, + "City": "Portland", + "State": "Maine", + "StateAbbreviation": "ME", + "DisplayName": "Portland, Maine", + "Latitude": 43.661471000000013, + "Longitude": -70.2553259 + }, + { + "Id": 520, + "City": "St. Cloud", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "St. Cloud, Minnesota", + "Latitude": 45.5579451, + "Longitude": -94.163240399999992 + }, + { + "Id": 521, + "City": "Davis", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Davis, California", + "Latitude": 38.5449065, + "Longitude": -121.7405167 + }, + { + "Id": 522, + "City": "South San Francisco", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "South San Francisco, California", + "Latitude": 37.654656, + "Longitude": -122.4077498 + }, + { + "Id": 523, + "City": "Camarillo", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Camarillo, California", + "Latitude": 34.2163937, + "Longitude": -119.0376023 + }, + { + "Id": 524, + "City": "North Little Rock", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "North Little Rock, Arkansas", + "Latitude": 34.769536, + "Longitude": -92.2670941 + }, + { + "Id": 525, + "City": "Schenectady", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Schenectady, New York", + "Latitude": 42.8142432, + "Longitude": -73.9395687 + }, + { + "Id": 526, + "City": "Gaithersburg", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Gaithersburg, Maryland", + "Latitude": 39.1434406, + "Longitude": -77.2013705 + }, + { + "Id": 527, + "City": "Harlingen", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Harlingen, Texas", + "Latitude": 26.1906306, + "Longitude": -97.696102599999989 + }, + { + "Id": 528, + "City": "Woodbury", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Woodbury, Minnesota", + "Latitude": 44.9238552, + "Longitude": -92.9593797 + }, + { + "Id": 529, + "City": "Eagan", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Eagan, Minnesota", + "Latitude": 44.8041322, + "Longitude": -93.1668858 + }, + { + "Id": 530, + "City": "Yuba City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Yuba City, California", + "Latitude": 39.1404477, + "Longitude": -121.6169108 + }, + { + "Id": 531, + "City": "Maple Grove", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Maple Grove, Minnesota", + "Latitude": 45.0724642, + "Longitude": -93.4557877 + }, + { + "Id": 532, + "City": "Youngstown", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Youngstown, Ohio", + "Latitude": 41.0997803, + "Longitude": -80.6495194 + }, + { + "Id": 533, + "City": "Skokie", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Skokie, Illinois", + "Latitude": 42.0324025, + "Longitude": -87.7416246 + }, + { + "Id": 534, + "City": "Kissimmee", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Kissimmee, Florida", + "Latitude": 28.2919557, + "Longitude": -81.40757099999999 + }, + { + "Id": 535, + "City": "Johnson City", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Johnson City, Tennessee", + "Latitude": 36.3134397, + "Longitude": -82.3534727 + }, + { + "Id": 536, + "City": "Victoria", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Victoria, Texas", + "Latitude": 28.8052674, + "Longitude": -97.0035982 + }, + { + "Id": 537, + "City": "San Clemente", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Clemente, California", + "Latitude": 33.4269728, + "Longitude": -117.6119925 + }, + { + "Id": 538, + "City": "Bayonne", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Bayonne, New Jersey", + "Latitude": 40.6687141, + "Longitude": -74.1143091 + }, + { + "Id": 539, + "City": "Laguna Niguel", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Laguna Niguel, California", + "Latitude": 33.5225261, + "Longitude": -117.7075526 + }, + { + "Id": 540, + "City": "East Orange", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "East Orange, New Jersey", + "Latitude": 40.767323, + "Longitude": -74.2048677 + }, + { + "Id": 541, + "City": "Shawnee", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Shawnee, Kansas", + "Latitude": 39.022848499999988, + "Longitude": -94.7151865 + }, + { + "Id": 542, + "City": "Homestead", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Homestead, Florida", + "Latitude": 25.4687224, + "Longitude": -80.4775569 + }, + { + "Id": 544, + "City": "Rockville", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Rockville, Maryland", + "Latitude": 39.0839973, + "Longitude": -77.1527578 + }, + { + "Id": 543, + "City": "Delray Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Delray Beach, Florida", + "Latitude": 26.4614625, + "Longitude": -80.0728201 + }, + { + "Id": 545, + "City": "Janesville", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Janesville, Wisconsin", + "Latitude": 42.6827885, + "Longitude": -89.0187222 + }, + { + "Id": 546, + "City": "Conway", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Conway, Arkansas", + "Latitude": 35.0886963, + "Longitude": -92.4421011 + }, + { + "Id": 547, + "City": "Pico Rivera", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Pico Rivera, California", + "Latitude": 33.9830688, + "Longitude": -118.096735 + }, + { + "Id": 548, + "City": "Lorain", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Lorain, Ohio", + "Latitude": 41.452819, + "Longitude": -82.1823746 + }, + { + "Id": 549, + "City": "Montebello", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Montebello, California", + "Latitude": 34.0165053, + "Longitude": -118.1137535 + }, + { + "Id": 550, + "City": "Lodi", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lodi, California", + "Latitude": 38.1341477, + "Longitude": -121.2722194 + }, + { + "Id": 551, + "City": "New Braunfels", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "New Braunfels, Texas", + "Latitude": 29.7030024, + "Longitude": -98.1244531 + }, + { + "Id": 552, + "City": "Marysville", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Marysville, Washington", + "Latitude": 48.0517637, + "Longitude": -122.1770818 + }, + { + "Id": 553, + "City": "Tamarac", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Tamarac, Florida", + "Latitude": 26.2128609, + "Longitude": -80.2497707 + }, + { + "Id": 554, + "City": "Madera", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Madera, California", + "Latitude": 36.9613356, + "Longitude": -120.0607176 + }, + { + "Id": 555, + "City": "Conroe", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Conroe, Texas", + "Latitude": 30.3118769, + "Longitude": -95.45605119999999 + }, + { + "Id": 556, + "City": "Santa Cruz", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santa Cruz, California", + "Latitude": 36.9741171, + "Longitude": -122.0307963 + }, + { + "Id": 557, + "City": "Eden Prairie", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Eden Prairie, Minnesota", + "Latitude": 44.8546856, + "Longitude": -93.47078599999999 + }, + { + "Id": 558, + "City": "Cheyenne", + "State": "Wyoming", + "StateAbbreviation": "WY", + "DisplayName": "Cheyenne, Wyoming", + "Latitude": 41.1399814, + "Longitude": -104.8202462 + }, + { + "Id": 559, + "City": "Daytona Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Daytona Beach, Florida", + "Latitude": 29.2108147, + "Longitude": -81.0228331 + }, + { + "Id": 560, + "City": "Alpharetta", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Alpharetta, Georgia", + "Latitude": 34.0753762, + "Longitude": -84.2940899 + }, + { + "Id": 561, + "City": "Hamilton", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Hamilton, Ohio", + "Latitude": 39.3995008, + "Longitude": -84.5613355 + }, + { + "Id": 562, + "City": "Waltham", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Waltham, Massachusetts", + "Latitude": 42.3764852, + "Longitude": -71.2356113 + }, + { + "Id": 563, + "City": "Coon Rapids", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Coon Rapids, Minnesota", + "Latitude": 45.1732394, + "Longitude": -93.303006299999993 + }, + { + "Id": 564, + "City": "Haverhill", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Haverhill, Massachusetts", + "Latitude": 42.7762015, + "Longitude": -71.0772796 + }, + { + "Id": 565, + "City": "Council Bluffs", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Council Bluffs, Iowa", + "Latitude": 41.2619444, + "Longitude": -95.8608333 + }, + { + "Id": 566, + "City": "Taylor", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Taylor, Michigan", + "Latitude": 42.240872, + "Longitude": -83.2696509 + }, + { + "Id": 567, + "City": "Utica", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Utica, New York", + "Latitude": 43.100903, + "Longitude": -75.232664 + }, + { + "Id": 568, + "City": "Ames", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Ames, Iowa", + "Latitude": 42.034722, + "Longitude": -93.61999999999999 + }, + { + "Id": 569, + "City": "La Habra", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "La Habra, California", + "Latitude": 33.9319578, + "Longitude": -117.9461734 + }, + { + "Id": 570, + "City": "Encinitas", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Encinitas, California", + "Latitude": 33.0369867, + "Longitude": -117.2919818 + }, + { + "Id": 571, + "City": "Bowling Green", + "State": "Kentucky", + "StateAbbreviation": "KY", + "DisplayName": "Bowling Green, Kentucky", + "Latitude": 36.9685219, + "Longitude": -86.4808043 + }, + { + "Id": 572, + "City": "Burnsville", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Burnsville, Minnesota", + "Latitude": 44.7677424, + "Longitude": -93.27772259999999 + }, + { + "Id": 573, + "City": "Greenville", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Greenville, South Carolina", + "Latitude": 34.852617599999988, + "Longitude": -82.3940104 + }, + { + "Id": 574, + "City": "West Des Moines", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "West Des Moines, Iowa", + "Latitude": 41.5772115, + "Longitude": -93.711332 + }, + { + "Id": 575, + "City": "Cedar Park", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Cedar Park, Texas", + "Latitude": 30.505198, + "Longitude": -97.8202888 + }, + { + "Id": 576, + "City": "Tulare", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Tulare, California", + "Latitude": 36.2077288, + "Longitude": -119.3473379 + }, + { + "Id": 577, + "City": "Monterey Park", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Monterey Park, California", + "Latitude": 34.0625106, + "Longitude": -118.1228476 + }, + { + "Id": 578, + "City": "Vineland", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Vineland, New Jersey", + "Latitude": 39.4863773, + "Longitude": -75.025963699999991 + }, + { + "Id": 579, + "City": "Terre Haute", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Terre Haute, Indiana", + "Latitude": 39.4667034, + "Longitude": -87.413909199999992 + }, + { + "Id": 580, + "City": "North Miami", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "North Miami, Florida", + "Latitude": 25.8900949, + "Longitude": -80.1867138 + }, + { + "Id": 581, + "City": "Mansfield", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Mansfield, Texas", + "Latitude": 32.5631924, + "Longitude": -97.1416768 + }, + { + "Id": 582, + "City": "West Allis", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "West Allis, Wisconsin", + "Latitude": 43.0166806, + "Longitude": -88.0070315 + }, + { + "Id": 583, + "City": "Bristol", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Bristol, Connecticut", + "Latitude": 41.671764800000012, + "Longitude": -72.9492703 + }, + { + "Id": 584, + "City": "Taylorsville", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Taylorsville, Utah", + "Latitude": 40.667724799999988, + "Longitude": -111.9388258 + }, + { + "Id": 585, + "City": "Malden", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Malden, Massachusetts", + "Latitude": 42.4250964, + "Longitude": -71.066163 + }, + { + "Id": 586, + "City": "Meriden", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Meriden, Connecticut", + "Latitude": 41.5381535, + "Longitude": -72.807043499999992 + }, + { + "Id": 587, + "City": "Blaine", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Blaine, Minnesota", + "Latitude": 45.1607987, + "Longitude": -93.234948899999992 + }, + { + "Id": 588, + "City": "Wellington", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Wellington, Florida", + "Latitude": 26.6617635, + "Longitude": -80.2683571 + }, + { + "Id": 589, + "City": "Cupertino", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Cupertino, California", + "Latitude": 37.3229978, + "Longitude": -122.0321823 + }, + { + "Id": 590, + "City": "Springfield", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Springfield, Oregon", + "Latitude": 44.0462362, + "Longitude": -123.0220289 + }, + { + "Id": 591, + "City": "Rogers", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Rogers, Arkansas", + "Latitude": 36.3320196, + "Longitude": -94.1185366 + }, + { + "Id": 592, + "City": "St. Clair Shores", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "St. Clair Shores, Michigan", + "Latitude": 42.4974085, + "Longitude": -82.896360399999992 + }, + { + "Id": 593, + "City": "Gardena", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Gardena, California", + "Latitude": 33.8883487, + "Longitude": -118.3089624 + }, + { + "Id": 594, + "City": "Pontiac", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Pontiac, Michigan", + "Latitude": 42.6389216, + "Longitude": -83.29104679999999 + }, + { + "Id": 595, + "City": "National City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "National City, California", + "Latitude": 32.6781085, + "Longitude": -117.0991967 + }, + { + "Id": 596, + "City": "Grand Junction", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Grand Junction, Colorado", + "Latitude": 39.0638705, + "Longitude": -108.5506486 + }, + { + "Id": 597, + "City": "Rocklin", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rocklin, California", + "Latitude": 38.7907339, + "Longitude": -121.2357828 + }, + { + "Id": 598, + "City": "Chapel Hill", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Chapel Hill, North Carolina", + "Latitude": 35.9131996, + "Longitude": -79.0558445 + }, + { + "Id": 599, + "City": "Casper", + "State": "Wyoming", + "StateAbbreviation": "WY", + "DisplayName": "Casper, Wyoming", + "Latitude": 42.866632, + "Longitude": -106.313081 + }, + { + "Id": 600, + "City": "Broomfield", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Broomfield, Colorado", + "Latitude": 39.9205411, + "Longitude": -105.0866504 + }, + { + "Id": 601, + "City": "Petaluma", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Petaluma, California", + "Latitude": 38.232417, + "Longitude": -122.6366524 + }, + { + "Id": 602, + "City": "South Jordan", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "South Jordan, Utah", + "Latitude": 40.5621704, + "Longitude": -111.929658 + }, + { + "Id": 603, + "City": "Springfield", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Springfield, Ohio", + "Latitude": 39.9242266, + "Longitude": -83.8088171 + }, + { + "Id": 604, + "City": "Great Falls", + "State": "Montana", + "StateAbbreviation": "MT", + "DisplayName": "Great Falls, Montana", + "Latitude": 47.4941836, + "Longitude": -111.2833449 + }, + { + "Id": 605, + "City": "Lancaster", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Lancaster, Pennsylvania", + "Latitude": 40.0378755, + "Longitude": -76.3055144 + }, + { + "Id": 606, + "City": "North Port", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "North Port, Florida", + "Latitude": 27.044224, + "Longitude": -82.2359254 + }, + { + "Id": 607, + "City": "Lakewood", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Lakewood, Washington", + "Latitude": 47.1717649, + "Longitude": -122.518458 + }, + { + "Id": 608, + "City": "Marietta", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Marietta, Georgia", + "Latitude": 33.952602000000013, + "Longitude": -84.5499327 + }, + { + "Id": 609, + "City": "San Rafael", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Rafael, California", + "Latitude": 37.9735346, + "Longitude": -122.5310874 + }, + { + "Id": 610, + "City": "Royal Oak", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Royal Oak, Michigan", + "Latitude": 42.4894801, + "Longitude": -83.1446485 + }, + { + "Id": 611, + "City": "Des Plaines", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Des Plaines, Illinois", + "Latitude": 42.0333623, + "Longitude": -87.883399099999991 + }, + { + "Id": 612, + "City": "Huntington Park", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Huntington Park, California", + "Latitude": 33.9816812, + "Longitude": -118.2250725 + }, + { + "Id": 613, + "City": "La Mesa", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "La Mesa, California", + "Latitude": 32.7678287, + "Longitude": -117.0230839 + }, + { + "Id": 614, + "City": "Orland Park", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Orland Park, Illinois", + "Latitude": 41.6303103, + "Longitude": -87.853942500000016 + }, + { + "Id": 615, + "City": "Auburn", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Auburn, Alabama", + "Latitude": 32.6098566, + "Longitude": -85.480782499999989 + }, + { + "Id": 616, + "City": "Lakeville", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Lakeville, Minnesota", + "Latitude": 44.6496868, + "Longitude": -93.242719999999991 + }, + { + "Id": 617, + "City": "Owensboro", + "State": "Kentucky", + "StateAbbreviation": "KY", + "DisplayName": "Owensboro, Kentucky", + "Latitude": 37.7719074, + "Longitude": -87.1111676 + }, + { + "Id": 618, + "City": "Moore", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Moore, Oklahoma", + "Latitude": 35.3395079, + "Longitude": -97.486702799999989 + }, + { + "Id": 619, + "City": "Jupiter", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Jupiter, Florida", + "Latitude": 26.9342246, + "Longitude": -80.0942087 + }, + { + "Id": 620, + "City": "Idaho Falls", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Idaho Falls, Idaho", + "Latitude": 43.491651399999988, + "Longitude": -112.0339645 + }, + { + "Id": 621, + "City": "Dubuque", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Dubuque, Iowa", + "Latitude": 42.5005583, + "Longitude": -90.66457179999999 + }, + { + "Id": 622, + "City": "Bartlett", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Bartlett, Tennessee", + "Latitude": 35.2045328, + "Longitude": -89.8739753 + }, + { + "Id": 623, + "City": "Rowlett", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Rowlett, Texas", + "Latitude": 32.9029017, + "Longitude": -96.56388 + }, + { + "Id": 624, + "City": "Novi", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Novi, Michigan", + "Latitude": 42.48059, + "Longitude": -83.4754913 + }, + { + "Id": 625, + "City": "White Plains", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "White Plains, New York", + "Latitude": 41.033986200000008, + "Longitude": -73.7629097 + }, + { + "Id": 626, + "City": "Arcadia", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Arcadia, California", + "Latitude": 34.1397292, + "Longitude": -118.0353449 + }, + { + "Id": 627, + "City": "Redmond", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Redmond, Washington", + "Latitude": 47.6739881, + "Longitude": -122.121512 + }, + { + "Id": 628, + "City": "Lake Elsinore", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lake Elsinore, California", + "Latitude": 33.6680772, + "Longitude": -117.3272615 + }, + { + "Id": 629, + "City": "Ocala", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Ocala, Florida", + "Latitude": 29.1871986, + "Longitude": -82.140092299999992 + }, + { + "Id": 630, + "City": "Tinley Park", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Tinley Park, Illinois", + "Latitude": 41.5731442, + "Longitude": -87.7932939 + }, + { + "Id": 631, + "City": "Port Orange", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Port Orange, Florida", + "Latitude": 29.1383165, + "Longitude": -80.9956105 + }, + { + "Id": 632, + "City": "Medford", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Medford, Massachusetts", + "Latitude": 42.4184296, + "Longitude": -71.1061639 + }, + { + "Id": 633, + "City": "Oak Lawn", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Oak Lawn, Illinois", + "Latitude": 41.719978, + "Longitude": -87.7479528 + }, + { + "Id": 634, + "City": "Rocky Mount", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Rocky Mount, North Carolina", + "Latitude": 35.9382103, + "Longitude": -77.7905339 + }, + { + "Id": 635, + "City": "Kokomo", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Kokomo, Indiana", + "Latitude": 40.486427, + "Longitude": -86.13360329999999 + }, + { + "Id": 636, + "City": "Coconut Creek", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Coconut Creek, Florida", + "Latitude": 26.2517482, + "Longitude": -80.17893509999999 + }, + { + "Id": 637, + "City": "Bowie", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Bowie, Maryland", + "Latitude": 39.0067768, + "Longitude": -76.779136499999993 + }, + { + "Id": 638, + "City": "Berwyn", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Berwyn, Illinois", + "Latitude": 41.850587399999988, + "Longitude": -87.7936685 + }, + { + "Id": 639, + "City": "Midwest City", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Midwest City, Oklahoma", + "Latitude": 35.4495065, + "Longitude": -97.3967019 + }, + { + "Id": 640, + "City": "Fountain Valley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Fountain Valley, California", + "Latitude": 33.7091847, + "Longitude": -117.9536697 + }, + { + "Id": 641, + "City": "Buckeye", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Buckeye, Arizona", + "Latitude": 33.3703197, + "Longitude": -112.5837766 + }, + { + "Id": 642, + "City": "Dearborn Heights", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Dearborn Heights, Michigan", + "Latitude": 42.3369816, + "Longitude": -83.273262699999989 + }, + { + "Id": 643, + "City": "Woodland", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Woodland, California", + "Latitude": 38.678515700000013, + "Longitude": -121.7732971 + }, + { + "Id": 644, + "City": "Noblesville", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Noblesville, Indiana", + "Latitude": 40.0455917, + "Longitude": -86.0085955 + }, + { + "Id": 645, + "City": "Valdosta", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Valdosta, Georgia", + "Latitude": 30.8327022, + "Longitude": -83.2784851 + }, + { + "Id": 646, + "City": "Diamond Bar", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Diamond Bar, California", + "Latitude": 34.0286226, + "Longitude": -117.8103367 + }, + { + "Id": 647, + "City": "Manhattan", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Manhattan, Kansas", + "Latitude": 39.183608199999988, + "Longitude": -96.57166939999999 + }, + { + "Id": 648, + "City": "Santee", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Santee, California", + "Latitude": 32.8383828, + "Longitude": -116.9739167 + }, + { + "Id": 649, + "City": "Taunton", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Taunton, Massachusetts", + "Latitude": 41.900101, + "Longitude": -71.0897674 + }, + { + "Id": 650, + "City": "Sanford", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Sanford, Florida", + "Latitude": 28.8028612, + "Longitude": -81.269453 + }, + { + "Id": 651, + "City": "Kettering", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Kettering, Ohio", + "Latitude": 39.689503599999988, + "Longitude": -84.1688274 + }, + { + "Id": 652, + "City": "New Brunswick", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "New Brunswick, New Jersey", + "Latitude": 40.4862157, + "Longitude": -74.4518188 + }, + { + "Id": 653, + "City": "Decatur", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Decatur, Alabama", + "Latitude": 34.6059253, + "Longitude": -86.9833417 + }, + { + "Id": 654, + "City": "Chicopee", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Chicopee, Massachusetts", + "Latitude": 42.1487043, + "Longitude": -72.6078672 + }, + { + "Id": 655, + "City": "Anderson", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Anderson, Indiana", + "Latitude": 40.1053196, + "Longitude": -85.6802541 + }, + { + "Id": 656, + "City": "Margate", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Margate, Florida", + "Latitude": 26.2445263, + "Longitude": -80.206436 + }, + { + "Id": 657, + "City": "Weymouth Town", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Weymouth Town, Massachusetts", + "Latitude": 42.2180724, + "Longitude": -70.941035599999992 + }, + { + "Id": 658, + "City": "Hempstead", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Hempstead, New York", + "Latitude": 40.7062128, + "Longitude": -73.6187397 + }, + { + "Id": 659, + "City": "Corvallis", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Corvallis, Oregon", + "Latitude": 44.5645659, + "Longitude": -123.2620435 + }, + { + "Id": 660, + "City": "Eastvale", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Eastvale, California", + "Latitude": 33.952463, + "Longitude": -117.5848025 + }, + { + "Id": 661, + "City": "Porterville", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Porterville, California", + "Latitude": 36.06523, + "Longitude": -119.0167679 + }, + { + "Id": 662, + "City": "West Haven", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "West Haven, Connecticut", + "Latitude": 41.2705484, + "Longitude": -72.9469711 + }, + { + "Id": 663, + "City": "Brentwood", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Brentwood, California", + "Latitude": 37.931868, + "Longitude": -121.6957863 + }, + { + "Id": 664, + "City": "Paramount", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Paramount, California", + "Latitude": 33.8894598, + "Longitude": -118.1597911 + }, + { + "Id": 665, + "City": "Grand Forks", + "State": "North Dakota", + "StateAbbreviation": "ND", + "DisplayName": "Grand Forks, North Dakota", + "Latitude": 47.9252568, + "Longitude": -97.0328547 + }, + { + "Id": 666, + "City": "Georgetown", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Georgetown, Texas", + "Latitude": 30.6332618, + "Longitude": -97.6779842 + }, + { + "Id": 667, + "City": "St. Peters", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "St. Peters, Missouri", + "Latitude": 38.7874699, + "Longitude": -90.6298922 + }, + { + "Id": 668, + "City": "Shoreline", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Shoreline, Washington", + "Latitude": 47.7556531, + "Longitude": -122.3415178 + }, + { + "Id": 669, + "City": "Mount Prospect", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Mount Prospect, Illinois", + "Latitude": 42.0664167, + "Longitude": -87.9372908 + }, + { + "Id": 670, + "City": "Hanford", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Hanford, California", + "Latitude": 36.3274502, + "Longitude": -119.6456844 + }, + { + "Id": 671, + "City": "Normal", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Normal, Illinois", + "Latitude": 40.5142026, + "Longitude": -88.9906312 + }, + { + "Id": 672, + "City": "Rosemead", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rosemead, California", + "Latitude": 34.0805651, + "Longitude": -118.072846 + }, + { + "Id": 673, + "City": "Lehi", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Lehi, Utah", + "Latitude": 40.3916172, + "Longitude": -111.8507662 + }, + { + "Id": 674, + "City": "Pocatello", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Pocatello, Idaho", + "Latitude": 42.8713032, + "Longitude": -112.4455344 + }, + { + "Id": 675, + "City": "Highland", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Highland, California", + "Latitude": 34.1283442, + "Longitude": -117.2086513 + }, + { + "Id": 676, + "City": "Novato", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Novato, California", + "Latitude": 38.1074198, + "Longitude": -122.5697032 + }, + { + "Id": 677, + "City": "Port Arthur", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Port Arthur, Texas", + "Latitude": 29.8849504, + "Longitude": -93.939946999999989 + }, + { + "Id": 678, + "City": "Carson City", + "State": "Nevada", + "StateAbbreviation": "NV", + "DisplayName": "Carson City, Nevada", + "Latitude": 39.1637984, + "Longitude": -119.7674034 + }, + { + "Id": 679, + "City": "San Marcos", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "San Marcos, Texas", + "Latitude": 29.8832749, + "Longitude": -97.9413941 + }, + { + "Id": 680, + "City": "Hendersonville", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Hendersonville, Tennessee", + "Latitude": 36.3047735, + "Longitude": -86.6199957 + }, + { + "Id": 681, + "City": "Elyria", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Elyria, Ohio", + "Latitude": 41.3683798, + "Longitude": -82.10764859999999 + }, + { + "Id": 682, + "City": "Revere", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Revere, Massachusetts", + "Latitude": 42.4084302, + "Longitude": -71.0119948 + }, + { + "Id": 683, + "City": "Pflugerville", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Pflugerville, Texas", + "Latitude": 30.4393696, + "Longitude": -97.620004299999991 + }, + { + "Id": 684, + "City": "Greenwood", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Greenwood, Indiana", + "Latitude": 39.6136578, + "Longitude": -86.10665259999999 + }, + { + "Id": 685, + "City": "Bellevue", + "State": "Nebraska", + "StateAbbreviation": "NE", + "DisplayName": "Bellevue, Nebraska", + "Latitude": 41.1543623, + "Longitude": -95.9145568 + }, + { + "Id": 686, + "City": "Wheaton", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Wheaton, Illinois", + "Latitude": 41.8661403, + "Longitude": -88.1070127 + }, + { + "Id": 687, + "City": "Smyrna", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Smyrna, Georgia", + "Latitude": 33.8839926, + "Longitude": -84.514376099999993 + }, + { + "Id": 688, + "City": "Sarasota", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Sarasota, Florida", + "Latitude": 27.3364347, + "Longitude": -82.53065269999999 + }, + { + "Id": 689, + "City": "Blue Springs", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Blue Springs, Missouri", + "Latitude": 39.0169509, + "Longitude": -94.2816148 + }, + { + "Id": 690, + "City": "Colton", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Colton, California", + "Latitude": 34.0739016, + "Longitude": -117.3136547 + }, + { + "Id": 691, + "City": "Euless", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Euless, Texas", + "Latitude": 32.8370727, + "Longitude": -97.08195409999999 + }, + { + "Id": 692, + "City": "Castle Rock", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Castle Rock, Colorado", + "Latitude": 39.3722121, + "Longitude": -104.8560902 + }, + { + "Id": 693, + "City": "Cathedral City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Cathedral City, California", + "Latitude": 33.7805388, + "Longitude": -116.4668036 + }, + { + "Id": 694, + "City": "Kingsport", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Kingsport, Tennessee", + "Latitude": 36.548434, + "Longitude": -82.5618186 + }, + { + "Id": 695, + "City": "Lake Havasu City", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Lake Havasu City, Arizona", + "Latitude": 34.483901, + "Longitude": -114.3224548 + }, + { + "Id": 696, + "City": "Pensacola", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Pensacola, Florida", + "Latitude": 30.42130899999999, + "Longitude": -87.2169149 + }, + { + "Id": 697, + "City": "Hoboken", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Hoboken, New Jersey", + "Latitude": 40.7439905, + "Longitude": -74.0323626 + }, + { + "Id": 698, + "City": "Yucaipa", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Yucaipa, California", + "Latitude": 34.033625, + "Longitude": -117.0430865 + }, + { + "Id": 699, + "City": "Watsonville", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Watsonville, California", + "Latitude": 36.910231, + "Longitude": -121.7568946 + }, + { + "Id": 700, + "City": "Richland", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Richland, Washington", + "Latitude": 46.2856907, + "Longitude": -119.2844621 + }, + { + "Id": 701, + "City": "Delano", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Delano, California", + "Latitude": 35.7688425, + "Longitude": -119.2470536 + }, + { + "Id": 702, + "City": "Hoffman Estates", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Hoffman Estates, Illinois", + "Latitude": 42.0629915, + "Longitude": -88.122719899999993 + }, + { + "Id": 703, + "City": "Florissant", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Florissant, Missouri", + "Latitude": 38.789217, + "Longitude": -90.322614 + }, + { + "Id": 704, + "City": "Placentia", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Placentia, California", + "Latitude": 33.8722371, + "Longitude": -117.8703363 + }, + { + "Id": 705, + "City": "West New York", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "West New York, New Jersey", + "Latitude": 40.7878788, + "Longitude": -74.0143064 + }, + { + "Id": 706, + "City": "Dublin", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Dublin, California", + "Latitude": 37.7021521, + "Longitude": -121.9357918 + }, + { + "Id": 707, + "City": "Oak Park", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Oak Park, Illinois", + "Latitude": 41.8850317, + "Longitude": -87.7845025 + }, + { + "Id": 708, + "City": "Peabody", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Peabody, Massachusetts", + "Latitude": 42.5278731, + "Longitude": -70.9286609 + }, + { + "Id": 709, + "City": "Perth Amboy", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Perth Amboy, New Jersey", + "Latitude": 40.5067723, + "Longitude": -74.2654234 + }, + { + "Id": 710, + "City": "Battle Creek", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Battle Creek, Michigan", + "Latitude": 42.3211522, + "Longitude": -85.179714199999992 + }, + { + "Id": 711, + "City": "Bradenton", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Bradenton, Florida", + "Latitude": 27.4989278, + "Longitude": -82.5748194 + }, + { + "Id": 712, + "City": "Gilroy", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Gilroy, California", + "Latitude": 37.0057816, + "Longitude": -121.5682751 + }, + { + "Id": 713, + "City": "Milford", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Milford, Connecticut", + "Latitude": 41.2306979, + "Longitude": -73.064036 + }, + { + "Id": 714, + "City": "Albany", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Albany, Oregon", + "Latitude": 44.6365107, + "Longitude": -123.1059282 + }, + { + "Id": 715, + "City": "Ankeny", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Ankeny, Iowa", + "Latitude": 41.7317884, + "Longitude": -93.6001278 + }, + { + "Id": 716, + "City": "La Crosse", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "La Crosse, Wisconsin", + "Latitude": 43.8013556, + "Longitude": -91.239580699999991 + }, + { + "Id": 717, + "City": "Burlington", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Burlington, North Carolina", + "Latitude": 36.0956918, + "Longitude": -79.437799099999992 + }, + { + "Id": 718, + "City": "DeSoto", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "DeSoto, Texas", + "Latitude": 32.5896998, + "Longitude": -96.8570738 + }, + { + "Id": 719, + "City": "Harrisonburg", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Harrisonburg, Virginia", + "Latitude": 38.4495688, + "Longitude": -78.8689155 + }, + { + "Id": 720, + "City": "Minnetonka", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Minnetonka, Minnesota", + "Latitude": 44.9211836, + "Longitude": -93.4687489 + }, + { + "Id": 721, + "City": "Elkhart", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Elkhart, Indiana", + "Latitude": 41.6819935, + "Longitude": -85.9766671 + }, + { + "Id": 722, + "City": "Lakewood", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Lakewood, Ohio", + "Latitude": 41.4819932, + "Longitude": -81.7981908 + }, + { + "Id": 723, + "City": "Glendora", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Glendora, California", + "Latitude": 34.1361187, + "Longitude": -117.865339 + }, + { + "Id": 724, + "City": "Southaven", + "State": "Mississippi", + "StateAbbreviation": "MS", + "DisplayName": "Southaven, Mississippi", + "Latitude": 34.9889818, + "Longitude": -90.0125913 + }, + { + "Id": 725, + "City": "Charleston", + "State": "West Virginia", + "StateAbbreviation": "WV", + "DisplayName": "Charleston, West Virginia", + "Latitude": 38.3498195, + "Longitude": -81.6326234 + }, + { + "Id": 726, + "City": "Joplin", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Joplin, Missouri", + "Latitude": 37.084227100000007, + "Longitude": -94.513280999999992 + }, + { + "Id": 727, + "City": "Enid", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Enid, Oklahoma", + "Latitude": 36.3955891, + "Longitude": -97.8783911 + }, + { + "Id": 728, + "City": "Palm Beach Gardens", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Palm Beach Gardens, Florida", + "Latitude": 26.8233946, + "Longitude": -80.138654699999989 + }, + { + "Id": 729, + "City": "Brookhaven", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Brookhaven, Georgia", + "Latitude": 33.8651033, + "Longitude": -84.3365917 + }, + { + "Id": 730, + "City": "Plainfield", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Plainfield, New Jersey", + "Latitude": 40.6337136, + "Longitude": -74.4073736 + }, + { + "Id": 731, + "City": "Grand Island", + "State": "Nebraska", + "StateAbbreviation": "NE", + "DisplayName": "Grand Island, Nebraska", + "Latitude": 40.9263957, + "Longitude": -98.3420118 + }, + { + "Id": 732, + "City": "Palm Desert", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Palm Desert, California", + "Latitude": 33.7222445, + "Longitude": -116.3744556 + }, + { + "Id": 733, + "City": "Huntersville", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Huntersville, North Carolina", + "Latitude": 35.410694, + "Longitude": -80.842850400000017 + }, + { + "Id": 734, + "City": "Tigard", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Tigard, Oregon", + "Latitude": 45.4312294, + "Longitude": -122.7714861 + }, + { + "Id": 735, + "City": "Lenexa", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Lenexa, Kansas", + "Latitude": 38.9536174, + "Longitude": -94.733570899999989 + }, + { + "Id": 736, + "City": "Saginaw", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Saginaw, Michigan", + "Latitude": 43.4194699, + "Longitude": -83.9508068 + }, + { + "Id": 737, + "City": "Kentwood", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Kentwood, Michigan", + "Latitude": 42.8694731, + "Longitude": -85.644749199999993 + }, + { + "Id": 738, + "City": "Doral", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Doral, Florida", + "Latitude": 25.8195424, + "Longitude": -80.3553302 + }, + { + "Id": 739, + "City": "Apple Valley", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Apple Valley, Minnesota", + "Latitude": 44.7319094, + "Longitude": -93.217720000000014 + }, + { + "Id": 740, + "City": "Grapevine", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Grapevine, Texas", + "Latitude": 32.9342919, + "Longitude": -97.0780654 + }, + { + "Id": 741, + "City": "Aliso Viejo", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Aliso Viejo, California", + "Latitude": 33.5676842, + "Longitude": -117.7256083 + }, + { + "Id": 742, + "City": "Sammamish", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Sammamish, Washington", + "Latitude": 47.616268299999987, + "Longitude": -122.0355736 + }, + { + "Id": 743, + "City": "Casa Grande", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Casa Grande, Arizona", + "Latitude": 32.8795022, + "Longitude": -111.7573521 + }, + { + "Id": 744, + "City": "Pinellas Park", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Pinellas Park, Florida", + "Latitude": 27.8428025, + "Longitude": -82.6995443 + }, + { + "Id": 745, + "City": "Troy", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Troy, New York", + "Latitude": 42.7284117, + "Longitude": -73.69178509999999 + }, + { + "Id": 746, + "City": "West Sacramento", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "West Sacramento, California", + "Latitude": 38.5804609, + "Longitude": -121.530234 + }, + { + "Id": 747, + "City": "Burien", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Burien, Washington", + "Latitude": 47.4703767, + "Longitude": -122.3467918 + }, + { + "Id": 748, + "City": "Commerce City", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Commerce City, Colorado", + "Latitude": 39.8083196, + "Longitude": -104.9338675 + }, + { + "Id": 749, + "City": "Monroe", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Monroe, Louisiana", + "Latitude": 32.5093109, + "Longitude": -92.1193012 + }, + { + "Id": 750, + "City": "Cerritos", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Cerritos, California", + "Latitude": 33.8583483, + "Longitude": -118.0647871 + }, + { + "Id": 751, + "City": "Downers Grove", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Downers Grove, Illinois", + "Latitude": 41.8089191, + "Longitude": -88.01117459999999 + }, + { + "Id": 752, + "City": "Coral Gables", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Coral Gables, Florida", + "Latitude": 25.72149, + "Longitude": -80.2683838 + }, + { + "Id": 753, + "City": "Wilson", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Wilson, North Carolina", + "Latitude": 35.7212689, + "Longitude": -77.9155395 + }, + { + "Id": 754, + "City": "Niagara Falls", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Niagara Falls, New York", + "Latitude": 43.0962143, + "Longitude": -79.0377388 + }, + { + "Id": 755, + "City": "Poway", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Poway, California", + "Latitude": 32.9628232, + "Longitude": -117.0358646 + }, + { + "Id": 756, + "City": "Edina", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Edina, Minnesota", + "Latitude": 44.8896866, + "Longitude": -93.3499489 + }, + { + "Id": 757, + "City": "Cuyahoga Falls", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Cuyahoga Falls, Ohio", + "Latitude": 41.1339449, + "Longitude": -81.484558499999991 + }, + { + "Id": 758, + "City": "Rancho Santa Margarita", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rancho Santa Margarita, California", + "Latitude": 33.640855, + "Longitude": -117.603104 + }, + { + "Id": 759, + "City": "Harrisburg", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Harrisburg, Pennsylvania", + "Latitude": 40.2731911, + "Longitude": -76.8867008 + }, + { + "Id": 760, + "City": "Huntington", + "State": "West Virginia", + "StateAbbreviation": "WV", + "DisplayName": "Huntington, West Virginia", + "Latitude": 38.4192496, + "Longitude": -82.445154000000016 + }, + { + "Id": 761, + "City": "La Mirada", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "La Mirada, California", + "Latitude": 33.9172357, + "Longitude": -118.0120086 + }, + { + "Id": 762, + "City": "Cypress", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Cypress, California", + "Latitude": 33.8169599, + "Longitude": -118.0372852 + }, + { + "Id": 763, + "City": "Caldwell", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Caldwell, Idaho", + "Latitude": 43.662938399999987, + "Longitude": -116.6873596 + }, + { + "Id": 764, + "City": "Logan", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Logan, Utah", + "Latitude": 41.7369803, + "Longitude": -111.8338359 + }, + { + "Id": 765, + "City": "Galveston", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Galveston, Texas", + "Latitude": 29.3013479, + "Longitude": -94.7976958 + }, + { + "Id": 766, + "City": "Sheboygan", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Sheboygan, Wisconsin", + "Latitude": 43.7508284, + "Longitude": -87.71453 + }, + { + "Id": 767, + "City": "Middletown", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Middletown, Ohio", + "Latitude": 39.5150576, + "Longitude": -84.398276299999992 + }, + { + "Id": 768, + "City": "Murray", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Murray, Utah", + "Latitude": 40.6668916, + "Longitude": -111.8879909 + }, + { + "Id": 769, + "City": "Roswell", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Roswell, New Mexico", + "Latitude": 33.3942655, + "Longitude": -104.5230242 + }, + { + "Id": 770, + "City": "Parker", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Parker, Colorado", + "Latitude": 39.5186002, + "Longitude": -104.7613633 + }, + { + "Id": 771, + "City": "Bedford", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Bedford, Texas", + "Latitude": 32.844017, + "Longitude": -97.1430671 + }, + { + "Id": 772, + "City": "East Lansing", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "East Lansing, Michigan", + "Latitude": 42.7369792, + "Longitude": -84.483865400000013 + }, + { + "Id": 773, + "City": "Methuen", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Methuen, Massachusetts", + "Latitude": 42.7262016, + "Longitude": -71.1908924 + }, + { + "Id": 774, + "City": "Covina", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Covina, California", + "Latitude": 34.0900091, + "Longitude": -117.8903397 + }, + { + "Id": 775, + "City": "Alexandria", + "State": "Louisiana", + "StateAbbreviation": "LA", + "DisplayName": "Alexandria, Louisiana", + "Latitude": 31.3112936, + "Longitude": -92.4451371 + }, + { + "Id": 776, + "City": "Olympia", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Olympia, Washington", + "Latitude": 47.0378741, + "Longitude": -122.9006951 + }, + { + "Id": 777, + "City": "Euclid", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Euclid, Ohio", + "Latitude": 41.5931049, + "Longitude": -81.5267873 + }, + { + "Id": 778, + "City": "Mishawaka", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Mishawaka, Indiana", + "Latitude": 41.6619927, + "Longitude": -86.15861559999999 + }, + { + "Id": 779, + "City": "Salina", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Salina, Kansas", + "Latitude": 38.8402805, + "Longitude": -97.611423699999989 + }, + { + "Id": 780, + "City": "Azusa", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Azusa, California", + "Latitude": 34.1336186, + "Longitude": -117.9075627 + }, + { + "Id": 781, + "City": "Newark", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Newark, Ohio", + "Latitude": 40.0581205, + "Longitude": -82.4012642 + }, + { + "Id": 782, + "City": "Chesterfield", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Chesterfield, Missouri", + "Latitude": 38.6631083, + "Longitude": -90.5770675 + }, + { + "Id": 783, + "City": "Leesburg", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Leesburg, Virginia", + "Latitude": 39.1156615, + "Longitude": -77.56360149999999 + }, + { + "Id": 784, + "City": "Dunwoody", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Dunwoody, Georgia", + "Latitude": 33.9462125, + "Longitude": -84.3346473 + }, + { + "Id": 785, + "City": "Hattiesburg", + "State": "Mississippi", + "StateAbbreviation": "MS", + "DisplayName": "Hattiesburg, Mississippi", + "Latitude": 31.3271189, + "Longitude": -89.290339199999991 + }, + { + "Id": 786, + "City": "Roseville", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Roseville, Michigan", + "Latitude": 42.4972583, + "Longitude": -82.9371409 + }, + { + "Id": 787, + "City": "Bonita Springs", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Bonita Springs, Florida", + "Latitude": 26.339806, + "Longitude": -81.7786972 + }, + { + "Id": 788, + "City": "Portage", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Portage, Michigan", + "Latitude": 42.2011538, + "Longitude": -85.5800022 + }, + { + "Id": 789, + "City": "St. Louis Park", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "St. Louis Park, Minnesota", + "Latitude": 44.9597376, + "Longitude": -93.3702186 + }, + { + "Id": 790, + "City": "Collierville", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Collierville, Tennessee", + "Latitude": 35.042036, + "Longitude": -89.6645266 + }, + { + "Id": 791, + "City": "Middletown", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Middletown, Connecticut", + "Latitude": 41.5623209, + "Longitude": -72.6506488 + }, + { + "Id": 792, + "City": "Stillwater", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Stillwater, Oklahoma", + "Latitude": 36.1156071, + "Longitude": -97.0583681 + }, + { + "Id": 793, + "City": "East Providence", + "State": "Rhode Island", + "StateAbbreviation": "RI", + "DisplayName": "East Providence, Rhode Island", + "Latitude": 41.8137116, + "Longitude": -71.3700545 + }, + { + "Id": 794, + "City": "Lawrence", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Lawrence, Indiana", + "Latitude": 39.8386516, + "Longitude": -86.0252612 + }, + { + "Id": 795, + "City": "Wauwatosa", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Wauwatosa, Wisconsin", + "Latitude": 43.0494572, + "Longitude": -88.0075875 + }, + { + "Id": 796, + "City": "Mentor", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Mentor, Ohio", + "Latitude": 41.6661573, + "Longitude": -81.339552 + }, + { + "Id": 797, + "City": "Ceres", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Ceres, California", + "Latitude": 37.5949316, + "Longitude": -120.9577098 + }, + { + "Id": 798, + "City": "Cedar Hill", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Cedar Hill, Texas", + "Latitude": 32.5884689, + "Longitude": -96.9561152 + }, + { + "Id": 799, + "City": "Mansfield", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Mansfield, Ohio", + "Latitude": 40.75839, + "Longitude": -82.5154471 + }, + { + "Id": 800, + "City": "Binghamton", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Binghamton, New York", + "Latitude": 42.098686699999988, + "Longitude": -75.917973800000013 + }, + { + "Id": 801, + "City": "Coeur d'Alene", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Coeur d'Alene, Idaho", + "Latitude": 47.6776832, + "Longitude": -116.7804664 + }, + { + "Id": 802, + "City": "San Luis Obispo", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Luis Obispo, California", + "Latitude": 35.2827524, + "Longitude": -120.6596156 + }, + { + "Id": 803, + "City": "Minot", + "State": "North Dakota", + "StateAbbreviation": "ND", + "DisplayName": "Minot, North Dakota", + "Latitude": 48.2329668, + "Longitude": -101.2922906 + }, + { + "Id": 804, + "City": "Palm Springs", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Palm Springs, California", + "Latitude": 33.8302961, + "Longitude": -116.5452921 + }, + { + "Id": 805, + "City": "Pine Bluff", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Pine Bluff, Arkansas", + "Latitude": 34.2284312, + "Longitude": -92.00319549999999 + }, + { + "Id": 806, + "City": "Texas City", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Texas City, Texas", + "Latitude": 29.383845, + "Longitude": -94.9027002 + }, + { + "Id": 807, + "City": "Summerville", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Summerville, South Carolina", + "Latitude": 33.0185039, + "Longitude": -80.175648099999989 + }, + { + "Id": 808, + "City": "Twin Falls", + "State": "Idaho", + "StateAbbreviation": "ID", + "DisplayName": "Twin Falls, Idaho", + "Latitude": 42.5629668, + "Longitude": -114.4608711 + }, + { + "Id": 809, + "City": "Jeffersonville", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Jeffersonville, Indiana", + "Latitude": 38.2775702, + "Longitude": -85.7371847 + }, + { + "Id": 810, + "City": "San Jacinto", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Jacinto, California", + "Latitude": 33.7839084, + "Longitude": -116.958635 + }, + { + "Id": 811, + "City": "Madison", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Madison, Alabama", + "Latitude": 34.6992579, + "Longitude": -86.748331800000017 + }, + { + "Id": 812, + "City": "Altoona", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Altoona, Pennsylvania", + "Latitude": 40.5186809, + "Longitude": -78.3947359 + }, + { + "Id": 813, + "City": "Columbus", + "State": "Indiana", + "StateAbbreviation": "IN", + "DisplayName": "Columbus, Indiana", + "Latitude": 39.2014404, + "Longitude": -85.9213796 + }, + { + "Id": 814, + "City": "Beavercreek", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Beavercreek, Ohio", + "Latitude": 39.7092262, + "Longitude": -84.063268499999992 + }, + { + "Id": 815, + "City": "Apopka", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Apopka, Florida", + "Latitude": 28.6934076, + "Longitude": -81.5322149 + }, + { + "Id": 816, + "City": "Elmhurst", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Elmhurst, Illinois", + "Latitude": 41.8994744, + "Longitude": -87.9403418 + }, + { + "Id": 817, + "City": "Maricopa", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Maricopa, Arizona", + "Latitude": 33.0581063, + "Longitude": -112.0476423 + }, + { + "Id": 818, + "City": "Farmington", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Farmington, New Mexico", + "Latitude": 36.728058300000008, + "Longitude": -108.2186856 + }, + { + "Id": 819, + "City": "Glenview", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Glenview, Illinois", + "Latitude": 42.0697509, + "Longitude": -87.7878408 + }, + { + "Id": 820, + "City": "Cleveland Heights", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Cleveland Heights, Ohio", + "Latitude": 41.5200518, + "Longitude": -81.556235 + }, + { + "Id": 821, + "City": "Draper", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Draper, Utah", + "Latitude": 40.5246711, + "Longitude": -111.8638226 + }, + { + "Id": 822, + "City": "Lincoln", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lincoln, California", + "Latitude": 38.891565, + "Longitude": -121.2930079 + }, + { + "Id": 823, + "City": "Sierra Vista", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Sierra Vista, Arizona", + "Latitude": 31.5455001, + "Longitude": -110.2772856 + }, + { + "Id": 824, + "City": "Lacey", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Lacey, Washington", + "Latitude": 47.034262899999987, + "Longitude": -122.8231915 + }, + { + "Id": 825, + "City": "Biloxi", + "State": "Mississippi", + "StateAbbreviation": "MS", + "DisplayName": "Biloxi, Mississippi", + "Latitude": 30.3960318, + "Longitude": -88.885307799999993 + }, + { + "Id": 826, + "City": "Strongsville", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Strongsville, Ohio", + "Latitude": 41.3144966, + "Longitude": -81.83569 + }, + { + "Id": 827, + "City": "Barnstable Town", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Barnstable Town, Massachusetts", + "Latitude": 41.7003208, + "Longitude": -70.3002024 + }, + { + "Id": 828, + "City": "Wylie", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Wylie, Texas", + "Latitude": 33.0151201, + "Longitude": -96.5388789 + }, + { + "Id": 829, + "City": "Sayreville", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Sayreville, New Jersey", + "Latitude": 40.459402100000013, + "Longitude": -74.360846 + }, + { + "Id": 830, + "City": "Kannapolis", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Kannapolis, North Carolina", + "Latitude": 35.4873613, + "Longitude": -80.6217341 + }, + { + "Id": 831, + "City": "Charlottesville", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Charlottesville, Virginia", + "Latitude": 38.0293059, + "Longitude": -78.476678100000015 + }, + { + "Id": 832, + "City": "Littleton", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Littleton, Colorado", + "Latitude": 39.613321, + "Longitude": -105.0166498 + }, + { + "Id": 833, + "City": "Titusville", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Titusville, Florida", + "Latitude": 28.6122187, + "Longitude": -80.8075537 + }, + { + "Id": 834, + "City": "Hackensack", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Hackensack, New Jersey", + "Latitude": 40.8859325, + "Longitude": -74.0434736 + }, + { + "Id": 835, + "City": "Newark", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Newark, California", + "Latitude": 37.5296593, + "Longitude": -122.0402399 + }, + { + "Id": 836, + "City": "Pittsfield", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Pittsfield, Massachusetts", + "Latitude": 42.4500845, + "Longitude": -73.2453824 + }, + { + "Id": 837, + "City": "York", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "York, Pennsylvania", + "Latitude": 39.9625984, + "Longitude": -76.727745 + }, + { + "Id": 838, + "City": "Lombard", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Lombard, Illinois", + "Latitude": 41.8800296, + "Longitude": -88.007843499999993 + }, + { + "Id": 839, + "City": "Attleboro", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Attleboro, Massachusetts", + "Latitude": 41.944544099999987, + "Longitude": -71.2856082 + }, + { + "Id": 840, + "City": "DeKalb", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "DeKalb, Illinois", + "Latitude": 41.9294736, + "Longitude": -88.750364699999992 + }, + { + "Id": 841, + "City": "Blacksburg", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Blacksburg, Virginia", + "Latitude": 37.2295733, + "Longitude": -80.4139393 + }, + { + "Id": 842, + "City": "Dublin", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Dublin, Ohio", + "Latitude": 40.0992294, + "Longitude": -83.1140771 + }, + { + "Id": 843, + "City": "Haltom City", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Haltom City, Texas", + "Latitude": 32.7995738, + "Longitude": -97.26918169999999 + }, + { + "Id": 844, + "City": "Lompoc", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Lompoc, California", + "Latitude": 34.6391501, + "Longitude": -120.4579409 + }, + { + "Id": 845, + "City": "El Centro", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "El Centro, California", + "Latitude": 32.792, + "Longitude": -115.5630514 + }, + { + "Id": 846, + "City": "Danville", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Danville, California", + "Latitude": 37.8215929, + "Longitude": -121.9999606 + }, + { + "Id": 847, + "City": "Jefferson City", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Jefferson City, Missouri", + "Latitude": 38.576701700000008, + "Longitude": -92.1735164 + }, + { + "Id": 848, + "City": "Cutler Bay", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Cutler Bay, Florida", + "Latitude": 25.5808323, + "Longitude": -80.346859299999991 + }, + { + "Id": 849, + "City": "Oakland Park", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Oakland Park, Florida", + "Latitude": 26.1723065, + "Longitude": -80.1319893 + }, + { + "Id": 850, + "City": "North Miami Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "North Miami Beach, Florida", + "Latitude": 25.9331488, + "Longitude": -80.1625463 + }, + { + "Id": 851, + "City": "Freeport", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Freeport, New York", + "Latitude": 40.6576022, + "Longitude": -73.58318349999999 + }, + { + "Id": 852, + "City": "Moline", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Moline, Illinois", + "Latitude": 41.5067003, + "Longitude": -90.515134199999991 + }, + { + "Id": 853, + "City": "Coachella", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Coachella, California", + "Latitude": 33.6803003, + "Longitude": -116.173894 + }, + { + "Id": 854, + "City": "Fort Pierce", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Fort Pierce, Florida", + "Latitude": 27.4467056, + "Longitude": -80.3256056 + }, + { + "Id": 855, + "City": "Smyrna", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Smyrna, Tennessee", + "Latitude": 35.9828412, + "Longitude": -86.5186045 + }, + { + "Id": 856, + "City": "Bountiful", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Bountiful, Utah", + "Latitude": 40.8893895, + "Longitude": -111.880771 + }, + { + "Id": 857, + "City": "Fond du Lac", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Fond du Lac, Wisconsin", + "Latitude": 43.7730448, + "Longitude": -88.4470508 + }, + { + "Id": 858, + "City": "Everett", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Everett, Massachusetts", + "Latitude": 42.40843, + "Longitude": -71.0536625 + }, + { + "Id": 859, + "City": "Danville", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Danville, Virginia", + "Latitude": 36.5859718, + "Longitude": -79.395022799999992 + }, + { + "Id": 860, + "City": "Keller", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Keller, Texas", + "Latitude": 32.9341893, + "Longitude": -97.229298 + }, + { + "Id": 861, + "City": "Belleville", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Belleville, Illinois", + "Latitude": 38.5200504, + "Longitude": -89.9839935 + }, + { + "Id": 862, + "City": "Bell Gardens", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Bell Gardens, California", + "Latitude": 33.9652918, + "Longitude": -118.1514588 + }, + { + "Id": 863, + "City": "Cleveland", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Cleveland, Tennessee", + "Latitude": 35.1595182, + "Longitude": -84.8766115 + }, + { + "Id": 864, + "City": "North Lauderdale", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "North Lauderdale, Florida", + "Latitude": 26.217305, + "Longitude": -80.2258811 + }, + { + "Id": 865, + "City": "Fairfield", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Fairfield, Ohio", + "Latitude": 39.3454673, + "Longitude": -84.5603187 + }, + { + "Id": 866, + "City": "Salem", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Salem, Massachusetts", + "Latitude": 42.51954, + "Longitude": -70.8967155 + }, + { + "Id": 867, + "City": "Rancho Palos Verdes", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rancho Palos Verdes, California", + "Latitude": 33.7444613, + "Longitude": -118.3870173 + }, + { + "Id": 868, + "City": "San Bruno", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Bruno, California", + "Latitude": 37.6304904, + "Longitude": -122.4110835 + }, + { + "Id": 869, + "City": "Concord", + "State": "New Hampshire", + "StateAbbreviation": "NH", + "DisplayName": "Concord, New Hampshire", + "Latitude": 43.2081366, + "Longitude": -71.5375718 + }, + { + "Id": 870, + "City": "Burlington", + "State": "Vermont", + "StateAbbreviation": "VT", + "DisplayName": "Burlington, Vermont", + "Latitude": 44.4758825, + "Longitude": -73.212071999999992 + }, + { + "Id": 871, + "City": "Apex", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Apex, North Carolina", + "Latitude": 35.732652, + "Longitude": -78.850285599999992 + }, + { + "Id": 872, + "City": "Midland", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Midland, Michigan", + "Latitude": 43.6155825, + "Longitude": -84.2472116 + }, + { + "Id": 873, + "City": "Altamonte Springs", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Altamonte Springs, Florida", + "Latitude": 28.6611089, + "Longitude": -81.3656242 + }, + { + "Id": 874, + "City": "Hutchinson", + "State": "Kansas", + "StateAbbreviation": "KS", + "DisplayName": "Hutchinson, Kansas", + "Latitude": 38.0608445, + "Longitude": -97.929774299999991 + }, + { + "Id": 875, + "City": "Buffalo Grove", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Buffalo Grove, Illinois", + "Latitude": 42.1662831, + "Longitude": -87.9631308 + }, + { + "Id": 876, + "City": "Urbandale", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Urbandale, Iowa", + "Latitude": 41.6266555, + "Longitude": -93.712165599999992 + }, + { + "Id": 877, + "City": "State College", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "State College, Pennsylvania", + "Latitude": 40.7933949, + "Longitude": -77.8600012 + }, + { + "Id": 878, + "City": "Urbana", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Urbana, Illinois", + "Latitude": 40.1105875, + "Longitude": -88.2072697 + }, + { + "Id": 879, + "City": "Plainfield", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Plainfield, Illinois", + "Latitude": 41.632223, + "Longitude": -88.2120315 + }, + { + "Id": 880, + "City": "Manassas", + "State": "Virginia", + "StateAbbreviation": "VA", + "DisplayName": "Manassas, Virginia", + "Latitude": 38.7509488, + "Longitude": -77.475266699999992 + }, + { + "Id": 881, + "City": "Bartlett", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Bartlett, Illinois", + "Latitude": 41.9950276, + "Longitude": -88.1856301 + }, + { + "Id": 882, + "City": "Kearny", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Kearny, New Jersey", + "Latitude": 40.7684342, + "Longitude": -74.1454214 + }, + { + "Id": 883, + "City": "Oro Valley", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Oro Valley, Arizona", + "Latitude": 32.3909071, + "Longitude": -110.966488 + }, + { + "Id": 884, + "City": "Findlay", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Findlay, Ohio", + "Latitude": 41.04422, + "Longitude": -83.6499321 + }, + { + "Id": 885, + "City": "Rohnert Park", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Rohnert Park, California", + "Latitude": 38.3396367, + "Longitude": -122.7010984 + }, + { + "Id": 887, + "City": "Westfield", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Westfield, Massachusetts", + "Latitude": 42.1250929, + "Longitude": -72.749538 + }, + { + "Id": 886, + "City": "Linden", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Linden, New Jersey", + "Latitude": 40.6220478, + "Longitude": -74.24459019999999 + }, + { + "Id": 888, + "City": "Sumter", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Sumter, South Carolina", + "Latitude": 33.9204354, + "Longitude": -80.3414693 + }, + { + "Id": 889, + "City": "Wilkes-Barre", + "State": "Pennsylvania", + "StateAbbreviation": "PA", + "DisplayName": "Wilkes-Barre, Pennsylvania", + "Latitude": 41.2459149, + "Longitude": -75.881307499999991 + }, + { + "Id": 890, + "City": "Woonsocket", + "State": "Rhode Island", + "StateAbbreviation": "RI", + "DisplayName": "Woonsocket, Rhode Island", + "Latitude": 42.002876099999988, + "Longitude": -71.514783900000012 + }, + { + "Id": 891, + "City": "Leominster", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Leominster, Massachusetts", + "Latitude": 42.5250906, + "Longitude": -71.759794 + }, + { + "Id": 892, + "City": "Shelton", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Shelton, Connecticut", + "Latitude": 41.3164856, + "Longitude": -73.0931641 + }, + { + "Id": 893, + "City": "Brea", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Brea, California", + "Latitude": 33.9166805, + "Longitude": -117.9000604 + }, + { + "Id": 894, + "City": "Covington", + "State": "Kentucky", + "StateAbbreviation": "KY", + "DisplayName": "Covington, Kentucky", + "Latitude": 39.0836712, + "Longitude": -84.5085536 + }, + { + "Id": 895, + "City": "Rockwall", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Rockwall, Texas", + "Latitude": 32.931233600000013, + "Longitude": -96.4597089 + }, + { + "Id": 896, + "City": "Meridian", + "State": "Mississippi", + "StateAbbreviation": "MS", + "DisplayName": "Meridian, Mississippi", + "Latitude": 32.3643098, + "Longitude": -88.703656 + }, + { + "Id": 897, + "City": "Riverton", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Riverton, Utah", + "Latitude": 40.521893, + "Longitude": -111.9391023 + }, + { + "Id": 898, + "City": "St. Cloud", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "St. Cloud, Florida", + "Latitude": 28.2489016, + "Longitude": -81.2811801 + }, + { + "Id": 899, + "City": "Quincy", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Quincy, Illinois", + "Latitude": 39.9356016, + "Longitude": -91.4098726 + }, + { + "Id": 900, + "City": "Morgan Hill", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Morgan Hill, California", + "Latitude": 37.1305012, + "Longitude": -121.6543901 + }, + { + "Id": 901, + "City": "Warren", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Warren, Ohio", + "Latitude": 41.2375569, + "Longitude": -80.818416599999992 + }, + { + "Id": 902, + "City": "Edmonds", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Edmonds, Washington", + "Latitude": 47.8106521, + "Longitude": -122.3773552 + }, + { + "Id": 903, + "City": "Burleson", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Burleson, Texas", + "Latitude": 32.5420821, + "Longitude": -97.3208492 + }, + { + "Id": 904, + "City": "Beverly", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Beverly, Massachusetts", + "Latitude": 42.5584283, + "Longitude": -70.880049 + }, + { + "Id": 905, + "City": "Mankato", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Mankato, Minnesota", + "Latitude": 44.1635775, + "Longitude": -93.99939959999999 + }, + { + "Id": 906, + "City": "Hagerstown", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Hagerstown, Maryland", + "Latitude": 39.6417629, + "Longitude": -77.71999319999999 + }, + { + "Id": 907, + "City": "Prescott", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Prescott, Arizona", + "Latitude": 34.5400242, + "Longitude": -112.4685025 + }, + { + "Id": 908, + "City": "Campbell", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Campbell, California", + "Latitude": 37.2871651, + "Longitude": -121.9499568 + }, + { + "Id": 909, + "City": "Cedar Falls", + "State": "Iowa", + "StateAbbreviation": "IA", + "DisplayName": "Cedar Falls, Iowa", + "Latitude": 42.5348993, + "Longitude": -92.4453161 + }, + { + "Id": 910, + "City": "Beaumont", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Beaumont, California", + "Latitude": 33.9294606, + "Longitude": -116.977248 + }, + { + "Id": 911, + "City": "La Puente", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "La Puente, California", + "Latitude": 34.0200114, + "Longitude": -117.9495083 + }, + { + "Id": 912, + "City": "Crystal Lake", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Crystal Lake, Illinois", + "Latitude": 42.2411344, + "Longitude": -88.31619649999999 + }, + { + "Id": 913, + "City": "Fitchburg", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Fitchburg, Massachusetts", + "Latitude": 42.5834228, + "Longitude": -71.8022955 + }, + { + "Id": 914, + "City": "Carol Stream", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Carol Stream, Illinois", + "Latitude": 41.912528599999987, + "Longitude": -88.134792699999991 + }, + { + "Id": 915, + "City": "Hickory", + "State": "North Carolina", + "StateAbbreviation": "NC", + "DisplayName": "Hickory, North Carolina", + "Latitude": 35.7344538, + "Longitude": -81.3444573 + }, + { + "Id": 916, + "City": "Streamwood", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Streamwood, Illinois", + "Latitude": 42.0255827, + "Longitude": -88.178408499999989 + }, + { + "Id": 917, + "City": "Norwich", + "State": "Connecticut", + "StateAbbreviation": "CT", + "DisplayName": "Norwich, Connecticut", + "Latitude": 41.5242649, + "Longitude": -72.075910499999992 + }, + { + "Id": 918, + "City": "Coppell", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Coppell, Texas", + "Latitude": 32.9545687, + "Longitude": -97.015007799999992 + }, + { + "Id": 919, + "City": "San Gabriel", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "San Gabriel, California", + "Latitude": 34.096111100000009, + "Longitude": -118.1058333 + }, + { + "Id": 920, + "City": "Holyoke", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Holyoke, Massachusetts", + "Latitude": 42.2042586, + "Longitude": -72.6162009 + }, + { + "Id": 921, + "City": "Bentonville", + "State": "Arkansas", + "StateAbbreviation": "AR", + "DisplayName": "Bentonville, Arkansas", + "Latitude": 36.3728538, + "Longitude": -94.2088172 + }, + { + "Id": 922, + "City": "Florence", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Florence, Alabama", + "Latitude": 34.79981, + "Longitude": -87.677251 + }, + { + "Id": 923, + "City": "Peachtree Corners", + "State": "Georgia", + "StateAbbreviation": "GA", + "DisplayName": "Peachtree Corners, Georgia", + "Latitude": 33.9698929, + "Longitude": -84.2214551 + }, + { + "Id": 924, + "City": "Brentwood", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Brentwood, Tennessee", + "Latitude": 36.0331164, + "Longitude": -86.782777200000012 + }, + { + "Id": 925, + "City": "Bozeman", + "State": "Montana", + "StateAbbreviation": "MT", + "DisplayName": "Bozeman, Montana", + "Latitude": 45.6769979, + "Longitude": -111.0429339 + }, + { + "Id": 926, + "City": "New Berlin", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "New Berlin, Wisconsin", + "Latitude": 42.9764027, + "Longitude": -88.1084224 + }, + { + "Id": 927, + "City": "Goose Creek", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Goose Creek, South Carolina", + "Latitude": 32.9810059, + "Longitude": -80.03258670000001 + }, + { + "Id": 928, + "City": "Huntsville", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Huntsville, Texas", + "Latitude": 30.7235263, + "Longitude": -95.550777099999991 + }, + { + "Id": 929, + "City": "Prescott Valley", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Prescott Valley, Arizona", + "Latitude": 34.6100243, + "Longitude": -112.315721 + }, + { + "Id": 930, + "City": "Maplewood", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Maplewood, Minnesota", + "Latitude": 44.9530215, + "Longitude": -92.9952153 + }, + { + "Id": 931, + "City": "Romeoville", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Romeoville, Illinois", + "Latitude": 41.6475306, + "Longitude": -88.0895061 + }, + { + "Id": 932, + "City": "Duncanville", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Duncanville, Texas", + "Latitude": 32.6518004, + "Longitude": -96.9083366 + }, + { + "Id": 933, + "City": "Atlantic City", + "State": "New Jersey", + "StateAbbreviation": "NJ", + "DisplayName": "Atlantic City, New Jersey", + "Latitude": 39.3642834, + "Longitude": -74.4229266 + }, + { + "Id": 934, + "City": "Clovis", + "State": "New Mexico", + "StateAbbreviation": "NM", + "DisplayName": "Clovis, New Mexico", + "Latitude": 34.4047987, + "Longitude": -103.2052272 + }, + { + "Id": 935, + "City": "The Colony", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "The Colony, Texas", + "Latitude": 33.0806083, + "Longitude": -96.892830899999993 + }, + { + "Id": 936, + "City": "Culver City", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Culver City, California", + "Latitude": 34.0211224, + "Longitude": -118.3964665 + }, + { + "Id": 937, + "City": "Marlborough", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Marlborough, Massachusetts", + "Latitude": 42.3459271, + "Longitude": -71.5522874 + }, + { + "Id": 938, + "City": "Hilton Head Island", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Hilton Head Island, South Carolina", + "Latitude": 32.216316, + "Longitude": -80.752608 + }, + { + "Id": 939, + "City": "Moorhead", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Moorhead, Minnesota", + "Latitude": 46.8737648, + "Longitude": -96.76780389999999 + }, + { + "Id": 940, + "City": "Calexico", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Calexico, California", + "Latitude": 32.6789476, + "Longitude": -115.4988834 + }, + { + "Id": 941, + "City": "Bullhead City", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Bullhead City, Arizona", + "Latitude": 35.1359386, + "Longitude": -114.5285981 + }, + { + "Id": 942, + "City": "Germantown", + "State": "Tennessee", + "StateAbbreviation": "TN", + "DisplayName": "Germantown, Tennessee", + "Latitude": 35.0867577, + "Longitude": -89.8100858 + }, + { + "Id": 943, + "City": "La Quinta", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "La Quinta, California", + "Latitude": 33.6633573, + "Longitude": -116.3100095 + }, + { + "Id": 944, + "City": "Lancaster", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Lancaster, Ohio", + "Latitude": 39.7136754, + "Longitude": -82.5993294 + }, + { + "Id": 945, + "City": "Wausau", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Wausau, Wisconsin", + "Latitude": 44.9591352, + "Longitude": -89.6301221 + }, + { + "Id": 946, + "City": "Sherman", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Sherman, Texas", + "Latitude": 33.6356618, + "Longitude": -96.6088805 + }, + { + "Id": 947, + "City": "Ocoee", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Ocoee, Florida", + "Latitude": 28.5691677, + "Longitude": -81.5439619 + }, + { + "Id": 948, + "City": "Shakopee", + "State": "Minnesota", + "StateAbbreviation": "MN", + "DisplayName": "Shakopee, Minnesota", + "Latitude": 44.7973962, + "Longitude": -93.5272861 + }, + { + "Id": 949, + "City": "Woburn", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Woburn, Massachusetts", + "Latitude": 42.4792618, + "Longitude": -71.1522765 + }, + { + "Id": 950, + "City": "Bremerton", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Bremerton, Washington", + "Latitude": 47.5673202, + "Longitude": -122.6329356 + }, + { + "Id": 951, + "City": "Rock Island", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Rock Island, Illinois", + "Latitude": 41.5094771, + "Longitude": -90.5787476 + }, + { + "Id": 952, + "City": "Muskogee", + "State": "Oklahoma", + "StateAbbreviation": "OK", + "DisplayName": "Muskogee, Oklahoma", + "Latitude": 35.7478769, + "Longitude": -95.3696909 + }, + { + "Id": 953, + "City": "Cape Girardeau", + "State": "Missouri", + "StateAbbreviation": "MO", + "DisplayName": "Cape Girardeau, Missouri", + "Latitude": 37.3058839, + "Longitude": -89.518147599999992 + }, + { + "Id": 954, + "City": "Annapolis", + "State": "Maryland", + "StateAbbreviation": "MD", + "DisplayName": "Annapolis, Maryland", + "Latitude": 38.9784453, + "Longitude": -76.4921829 + }, + { + "Id": 955, + "City": "Greenacres", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Greenacres, Florida", + "Latitude": 26.6276276, + "Longitude": -80.1353896 + }, + { + "Id": 956, + "City": "Ormond Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Ormond Beach, Florida", + "Latitude": 29.2858129, + "Longitude": -81.0558894 + }, + { + "Id": 957, + "City": "Hallandale Beach", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Hallandale Beach, Florida", + "Latitude": 25.9812024, + "Longitude": -80.148378999999991 + }, + { + "Id": 958, + "City": "Stanton", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Stanton, California", + "Latitude": 33.8025155, + "Longitude": -117.9931165 + }, + { + "Id": 959, + "City": "Puyallup", + "State": "Washington", + "StateAbbreviation": "WA", + "DisplayName": "Puyallup, Washington", + "Latitude": 47.1853785, + "Longitude": -122.2928974 + }, + { + "Id": 960, + "City": "Pacifica", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Pacifica, California", + "Latitude": 37.6138253, + "Longitude": -122.4869194 + }, + { + "Id": 961, + "City": "Hanover Park", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Hanover Park, Illinois", + "Latitude": 41.9994722, + "Longitude": -88.1450735 + }, + { + "Id": 962, + "City": "Hurst", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Hurst, Texas", + "Latitude": 32.8234621, + "Longitude": -97.1705678 + }, + { + "Id": 963, + "City": "Lima", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Lima, Ohio", + "Latitude": 40.742551, + "Longitude": -84.1052256 + }, + { + "Id": 964, + "City": "Marana", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Marana, Arizona", + "Latitude": 32.436381, + "Longitude": -111.2224422 + }, + { + "Id": 965, + "City": "Carpentersville", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Carpentersville, Illinois", + "Latitude": 42.1211364, + "Longitude": -88.2578582 + }, + { + "Id": 966, + "City": "Oakley", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Oakley, California", + "Latitude": 37.9974219, + "Longitude": -121.7124536 + }, + { + "Id": 967, + "City": "Huber Heights", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Huber Heights, Ohio", + "Latitude": 39.843947, + "Longitude": -84.124660800000015 + }, + { + "Id": 968, + "City": "Lancaster", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Lancaster, Texas", + "Latitude": 32.5920798, + "Longitude": -96.7561082 + }, + { + "Id": 969, + "City": "Montclair", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Montclair, California", + "Latitude": 34.0775104, + "Longitude": -117.6897776 + }, + { + "Id": 970, + "City": "Wheeling", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Wheeling, Illinois", + "Latitude": 42.1391927, + "Longitude": -87.9289591 + }, + { + "Id": 971, + "City": "Brookfield", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Brookfield, Wisconsin", + "Latitude": 43.0605671, + "Longitude": -88.1064787 + }, + { + "Id": 972, + "City": "Park Ridge", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Park Ridge, Illinois", + "Latitude": 42.0111412, + "Longitude": -87.840619199999992 + }, + { + "Id": 973, + "City": "Florence", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Florence, South Carolina", + "Latitude": 34.1954331, + "Longitude": -79.7625625 + }, + { + "Id": 974, + "City": "Roy", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Roy, Utah", + "Latitude": 41.1616108, + "Longitude": -112.0263313 + }, + { + "Id": 975, + "City": "Winter Garden", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Winter Garden, Florida", + "Latitude": 28.5652787, + "Longitude": -81.58618469999999 + }, + { + "Id": 976, + "City": "Chelsea", + "State": "Massachusetts", + "StateAbbreviation": "MA", + "DisplayName": "Chelsea, Massachusetts", + "Latitude": 42.3917638, + "Longitude": -71.0328284 + }, + { + "Id": 977, + "City": "Valley Stream", + "State": "New York", + "StateAbbreviation": "NY", + "DisplayName": "Valley Stream, New York", + "Latitude": 40.6642699, + "Longitude": -73.708464499999991 + }, + { + "Id": 978, + "City": "Spartanburg", + "State": "South Carolina", + "StateAbbreviation": "SC", + "DisplayName": "Spartanburg, South Carolina", + "Latitude": 34.9495672, + "Longitude": -81.9320482 + }, + { + "Id": 979, + "City": "Lake Oswego", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Lake Oswego, Oregon", + "Latitude": 45.420674899999987, + "Longitude": -122.6706498 + }, + { + "Id": 980, + "City": "Friendswood", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Friendswood, Texas", + "Latitude": 29.5293998, + "Longitude": -95.2010447 + }, + { + "Id": 981, + "City": "Westerville", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Westerville, Ohio", + "Latitude": 40.1261743, + "Longitude": -82.929069599999991 + }, + { + "Id": 982, + "City": "Northglenn", + "State": "Colorado", + "StateAbbreviation": "CO", + "DisplayName": "Northglenn, Colorado", + "Latitude": 39.8961821, + "Longitude": -104.9811468 + }, + { + "Id": 983, + "City": "Phenix City", + "State": "Alabama", + "StateAbbreviation": "AL", + "DisplayName": "Phenix City, Alabama", + "Latitude": 32.4709761, + "Longitude": -85.0007653 + }, + { + "Id": 984, + "City": "Grove City", + "State": "Ohio", + "StateAbbreviation": "OH", + "DisplayName": "Grove City, Ohio", + "Latitude": 39.881451899999988, + "Longitude": -83.0929644 + }, + { + "Id": 985, + "City": "Texarkana", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Texarkana, Texas", + "Latitude": 33.425125, + "Longitude": -94.04768820000001 + }, + { + "Id": 986, + "City": "Addison", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Addison, Illinois", + "Latitude": 41.931696, + "Longitude": -87.9889556 + }, + { + "Id": 987, + "City": "Dover", + "State": "Delaware", + "StateAbbreviation": "DE", + "DisplayName": "Dover, Delaware", + "Latitude": 39.158168, + "Longitude": -75.5243682 + }, + { + "Id": 988, + "City": "Lincoln Park", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Lincoln Park, Michigan", + "Latitude": 42.2505943, + "Longitude": -83.1785361 + }, + { + "Id": 989, + "City": "Calumet City", + "State": "Illinois", + "StateAbbreviation": "IL", + "DisplayName": "Calumet City, Illinois", + "Latitude": 41.6155909, + "Longitude": -87.5294871 + }, + { + "Id": 990, + "City": "Muskegon", + "State": "Michigan", + "StateAbbreviation": "MI", + "DisplayName": "Muskegon, Michigan", + "Latitude": 43.2341813, + "Longitude": -86.24839209999999 + }, + { + "Id": 991, + "City": "Aventura", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Aventura, Florida", + "Latitude": 25.9564812, + "Longitude": -80.1392121 + }, + { + "Id": 992, + "City": "Martinez", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Martinez, California", + "Latitude": 38.0193657, + "Longitude": -122.1341321 + }, + { + "Id": 993, + "City": "Greenfield", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Greenfield, Wisconsin", + "Latitude": 42.9614039, + "Longitude": -88.0125865 + }, + { + "Id": 994, + "City": "Apache Junction", + "State": "Arizona", + "StateAbbreviation": "AZ", + "DisplayName": "Apache Junction, Arizona", + "Latitude": 33.4150485, + "Longitude": -111.5495777 + }, + { + "Id": 995, + "City": "Monrovia", + "State": "California", + "StateAbbreviation": "CA", + "DisplayName": "Monrovia, California", + "Latitude": 34.1442616, + "Longitude": -118.0019482 + }, + { + "Id": 996, + "City": "Weslaco", + "State": "Texas", + "StateAbbreviation": "TX", + "DisplayName": "Weslaco, Texas", + "Latitude": 26.1595194, + "Longitude": -97.9908366 + }, + { + "Id": 997, + "City": "Keizer", + "State": "Oregon", + "StateAbbreviation": "OR", + "DisplayName": "Keizer, Oregon", + "Latitude": 44.9901194, + "Longitude": -123.0262077 + }, + { + "Id": 998, + "City": "Spanish Fork", + "State": "Utah", + "StateAbbreviation": "UT", + "DisplayName": "Spanish Fork, Utah", + "Latitude": 40.114955, + "Longitude": -111.654923 + }, + { + "Id": 999, + "City": "Beloit", + "State": "Wisconsin", + "StateAbbreviation": "WI", + "DisplayName": "Beloit, Wisconsin", + "Latitude": 42.5083482, + "Longitude": -89.031776499999992 + }, + { + "Id": 1000, + "City": "Panama City", + "State": "Florida", + "StateAbbreviation": "FL", + "DisplayName": "Panama City, Florida", + "Latitude": 30.1588129, + "Longitude": -85.6602058 + } +] \ No newline at end of file diff --git a/SlackBotPrototype/README.md b/SlackBotPrototype/README.md index 16d8048..fe1a771 100644 --- a/SlackBotPrototype/README.md +++ b/SlackBotPrototype/README.md @@ -2,11 +2,17 @@ ## Overview -Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it'll notify every morning of the current weather if the today's forecast is different from yesterday's. The bot is currently intended to run a console executable but can be modified to run as a windows service. +Slack weather bot that uses DarkSky API to get forecasts. The bot supports location based weather requests () +To support location based weather requests, we need to persist user locations. + +It currently only supports small set of locations from demonstration purposes, 1000 cities in the United States only. Data source for cities from this Gist: https://gist.github.com/Miserlou/c5cd8364bf9b2420bb29 ## Features * Responds to `weather now`, `weather tomorrow`, and `what should I wear?` +* Responds to `weather now `, `weather tomorrow ` + * needs to be a city state pair, where state is a two letter abbreviation e.g. New York, NY +* User can set location with `set me to ` ## Development Setup @@ -17,18 +23,40 @@ Slack weather bot that uses DarkSky API to get forecasts. If bot is running, it' * DarkSky API -- provides the weather info * Slack API -- uses RTM inferface -* Stanford NLP library -- used for tokenization and labeling of messages - * Download model data package from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip +* SQLite database ## Setup Instructions -* Download Stanford NLP model data from http://nlp.stanford.edu/software/stanford-postagger-full-2016-10-31.zip -* Extract package contents -* Set environment variable for Stanford NLP model folder `STANFORD_NLP_FOLDER` to the location of the extracted package -* Set environment variable for DarkSky API `DARK_SKY_TOKEN` to DarkSky secret -* Set environment variable for Slack API bot user `SLACK_API_TOKEN` to Slack bot user secret -* Run executable via commandline `./SlackBotPrototype.exe` - +1. Create app.config file in deploy folder +``` + + + + + + + + + + + + + + + + + + +``` +1. Set token for DarkSky API `DARK_SKY_TOKEN` to DarkSky secret in App.config +1. Set token for Slack API bot user `SLACK_API_TOKEN` to Slack bot user secret in App.config +1. Run executable `./DBPreparer.exe`, this creates a `SlackBotDb.db` file that is required for persistence. +1. Copy DB file to deploy folder. +1. Run executable via commandline `./SlackBotPrototype.exe` + +## Troubleshooting + +* `SQLite.dll` is not found -- quirk with library means that sometimes a full `rebuid solution` is required diff --git a/SlackBotPrototype/SlackBotPrototype.sln b/SlackBotPrototype/SlackBotPrototype.sln index ee81802..477a2a5 100644 --- a/SlackBotPrototype/SlackBotPrototype.sln +++ b/SlackBotPrototype/SlackBotPrototype.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Tests", "API.Tests\API.Tests.csproj", "{BEAB33E8-9F1E-4D39-B741-2069E95834DD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBPreparer", "DBPreparer\DBPreparer.csproj", "{41D05933-278A-45E7-A64D-933527E27883}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEAB33E8-9F1E-4D39-B741-2069E95834DD}.Release|Any CPU.Build.0 = Release|Any CPU + {41D05933-278A-45E7-A64D-933527E27883}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41D05933-278A-45E7-A64D-933527E27883}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41D05933-278A-45E7-A64D-933527E27883}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41D05933-278A-45E7-A64D-933527E27883}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SlackBotPrototype/SlackBotPrototype/App.config b/SlackBotPrototype/SlackBotPrototype/App.config deleted file mode 100644 index cd9f668..0000000 --- a/SlackBotPrototype/SlackBotPrototype/App.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SlackBotPrototype/SlackBotPrototype/Program.cs b/SlackBotPrototype/SlackBotPrototype/Program.cs index 59abc39..62199cd 100644 --- a/SlackBotPrototype/SlackBotPrototype/Program.cs +++ b/SlackBotPrototype/SlackBotPrototype/Program.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading; using API; -using FluentScheduler; using SlackAPI; using SlackAPI.WebSocketMessages; @@ -11,20 +10,15 @@ namespace SlackBotPrototype internal static class Program { private static SlackSocketClient _client; - private static IMessageParser _messageParser; + private static IMessageToCommandConverter _messageToCommandConverter; private static IWeatherProvider _weatherProvider; - //1. Can assume that all requests are for one location(no need to manage individual user location...an instance of the bot can serve "Washington, DC" only). - //2. Respond to two commands triggered upon mention. "Weather now" "Weather tomorrow". They do what you'd expect. - //3. When the weather is going to be materially different from yesterday, let @channel know in the morning. - //4. One embellishment of your choice. Determine a feature you think this bot should have, and implement it. private static void Main(string[] args) { var clientReady = new ManualResetEventSlim(false); var slackToken = ConfigConstants.SlackApiSecret; var darkSkyToken = ConfigConstants.DarkSkyApiSecret; - var nlpFolder = ConfigConstants.StanfordNlpFolder; - + if (string.IsNullOrWhiteSpace(slackToken)) { // normally would use logging library instead @@ -32,12 +26,6 @@ private static void Main(string[] args) return; } - if (string.IsNullOrWhiteSpace(nlpFolder)) - { - Console.WriteLine("NLP folder not found in environment var name = `{STANFORD_NLP_FOLDER}`"); - return; - } - if (string.IsNullOrWhiteSpace(darkSkyToken)) { Console.WriteLine("Dark sky API token not available var name = `{DARK_SKY_TOKEN}`"); @@ -45,8 +33,8 @@ private static void Main(string[] args) } _client = new SlackSocketClient(slackToken); - _messageParser = new MessageParser(nlpFolder); - _weatherProvider = new DarkSkyWeatherProvider(darkSkyToken); + _weatherProvider = new DarkSkyWeatherProvider(darkSkyToken, new RestClientHttpProvider(ConfigConstants.DarkSkyBaseUrl)); + _messageToCommandConverter = new MessageToCommandConverter(_weatherProvider, new SqLitePersistence()); _client.Connect((connected) => { // This is called once the client has emitted the RTM start command @@ -57,24 +45,10 @@ private static void Main(string[] args) // This is called once the RTM client has connected to the end point Console.WriteLine("RTM connected"); }); - - Channel[] channels; - _client.GetChannelList((response) => - { - channels = response.channels.Where(c => c.is_member).ToArray(); - - JobManager.AddJob(() => - { - var forecastWarning = _weatherProvider.GetForecastWarning(); - - if (channels.Length <= 0 || string.IsNullOrWhiteSpace(forecastWarning)) return; - foreach (var channel in channels) - { - _client.PostMessage((r) => { }, channel.id, $"@channel Notification: weather changed -- {forecastWarning}", linkNames: true); - } - - }, (s) => s.ToRunEvery(1).Days().At(6, 0)); + _client.GetUserList((ulr) => + { + Console.WriteLine("Got users"); }); _client.OnMessageReceived += OnClientOnOnMessageReceived; @@ -87,12 +61,14 @@ private static void OnClientOnOnMessageReceived(NewMessage message) { Console.WriteLine(_client.MyData.name); Console.WriteLine(message.type + " " + message.text); - + if (message.text.Contains($"@{_client.MyData.id}")) { - Console.WriteLine("Mentioned"); - var resp = _messageParser.HandleRequest(message.text); - _client.PostMessage((response) => { }, message.channel, !string.IsNullOrWhiteSpace(resp) ? $"{resp}" : "Huh?"); + Console.WriteLine($"Mentioned by {message.user}:{message.id}"); + var resp = _messageToCommandConverter.HandleRequest(message.user, message.text); + var msg = resp.Command.Invoke(); + + _client.PostMessage((response) => { }, message.channel, !string.IsNullOrWhiteSpace(msg) ? msg : "Huh?"); } else { diff --git a/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj b/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj index 7357f58..8cab2a5 100644 --- a/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj +++ b/SlackBotPrototype/SlackBotPrototype/SlackBotPrototype.csproj @@ -35,12 +35,99 @@ ..\packages\FluentScheduler.5.3.0\lib\net40\FluentScheduler.dll + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.AWT.WinForms.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Beans.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Charsets.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Cldrdata.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Corba.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Core.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Jdbc.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Localedata.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Management.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Media.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Misc.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Naming.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Nashorn.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Remoting.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Security.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.SwingAWT.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Text.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Tools.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.Util.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.API.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Bind.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Crypto.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Parse.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.Transform.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.WebServices.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.OpenJDK.XML.XPath.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.Runtime.dll + + + ..\packages\IKVM.8.1.5717.0\lib\IKVM.Runtime.JNI.dll + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\SlackAPI.1.0.5.93\lib\net45\SlackAPI.dll + + ..\packages\Stanford.NLP.POSTagger.3.7.0.1\lib\stanford-postagger-3.7.0.dll + diff --git a/SlackBotPrototype/SlackBotPrototype/packages.config b/SlackBotPrototype/SlackBotPrototype/packages.config index 415c695..133d274 100644 --- a/SlackBotPrototype/SlackBotPrototype/packages.config +++ b/SlackBotPrototype/SlackBotPrototype/packages.config @@ -1,6 +1,8 @@  + + \ No newline at end of file From 1750f20b5f517a8e02999bbf113df7554a069d0b Mon Sep 17 00:00:00 2001 From: kdt4242 Date: Sun, 6 Aug 2017 09:19:31 -0400 Subject: [PATCH 7/8] Update readme --- SlackBotPrototype/README.md | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/SlackBotPrototype/README.md b/SlackBotPrototype/README.md index fe1a771..861daa6 100644 --- a/SlackBotPrototype/README.md +++ b/SlackBotPrototype/README.md @@ -18,6 +18,46 @@ It currently only supports small set of locations from demonstration purposes, 1 * Visual Studio 2017 * Windows 10 (.NET 4.5.2) +* Ensure that app.config files are present in both `DBPreparer` and `SlackBotPropertype` + +### DBPreparer App.config + +``` + + + + + + + + + + + +``` + +### SlackBotPrototype App.config + +``` + + + + + + + + + + + + + + + + + + +``` ## External Dependencies From 3a621a6b1accb7c0542f761e157adb0c6ef22279 Mon Sep 17 00:00:00 2001 From: kdt4242 Date: Sun, 6 Aug 2017 09:27:33 -0400 Subject: [PATCH 8/8] Documentation. --- SlackBotPrototype/SlackBotPrototype/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SlackBotPrototype/SlackBotPrototype/Program.cs b/SlackBotPrototype/SlackBotPrototype/Program.cs index 62199cd..8c9c0e2 100644 --- a/SlackBotPrototype/SlackBotPrototype/Program.cs +++ b/SlackBotPrototype/SlackBotPrototype/Program.cs @@ -21,7 +21,7 @@ private static void Main(string[] args) if (string.IsNullOrWhiteSpace(slackToken)) { - // normally would use logging library instead + // SHORTCUT: Normally would use logging library instead so that we can have a robust, and standardized logs Console.WriteLine("Slack token not found in environment var name = `{SLACK_API_TOKEN}`"); return; } @@ -32,6 +32,8 @@ private static void Main(string[] args) return; } + // SHORTCUT: We'd normally wrap the slack client library so that we can + // switch implementations or libraries with less fuss _client = new SlackSocketClient(slackToken); _weatherProvider = new DarkSkyWeatherProvider(darkSkyToken, new RestClientHttpProvider(ConfigConstants.DarkSkyBaseUrl)); _messageToCommandConverter = new MessageToCommandConverter(_weatherProvider, new SqLitePersistence());