Updated to version: 1.0.0.2
A
Time Zone is a region that has a uniform standard time for legal, commercial, and social purposes. It is convenient for areas in close commercial or other communication to keep the same time, so time zones tend to follow the boundaries of countries and their subdivisions.
The code snippet proposed here can be used to
finding TimeZone by geographical coordinate Latitude / Longitude using a TimeZone Shapefile database on .NET framework
The TimeZone shapefile used for conversion is:
All the library used are open source, also you can find License for those library inside the sample project attached to this post:
At first we need to load all the Geometries contained in the shapefile, and the TimeZone associated, we build a structure used as the container for all data:
private List<GetTimeZones> m_geotimezones = new List<GetTimeZones>();
private class GetTimeZones
{
public string timezone { get; set; }
public GeoAPI.Geometries.IGeometry[] geometry { get; set; }
}
Then we load all the data to this container:
public FindTimeZoneByLocation(string shapeFileName)
{
try
{
List<NetTopologySuite.Features.Feature> featureCollection = new List<NetTopologySuite.Features.Feature>();
//built feature collection from shapefile
NetTopologySuite.IO.ShapefileDataReader dataReader = new NetTopologySuite.IO.ShapefileDataReader(shapeFileName, new NetTopologySuite.Geometries.GeometryFactory());
while (dataReader.Read())
{
//read current feature
NetTopologySuite.Features.Feature feature = new NetTopologySuite.Features.Feature();
feature.Geometry = dataReader.Geometry;
//get feature keys
int length = dataReader.DbaseHeader.NumFields;
string[] keys = new string[length];
for (int i = 0; i < length; i++)
keys[i] = dataReader.DbaseHeader.Fields[i].Name;
//get features attributes
feature.Attributes = new NetTopologySuite.Features.AttributesTable();
for (int i = 0; i < length; i++)
{
object val = dataReader.GetValue(i);
feature.Attributes.AddAttribute(keys[i], val);
}
//add feature to collection
featureCollection.Add(feature);
}
GeoAPI.Geometries.IGeometryFactory gf = GeoAPI.GeometryServiceProvider.Instance.CreateGeometryFactory();
//built timezones array
foreach (NetTopologySuite.Features.Feature feature in featureCollection)
{
try
{
//get timezone
NetTopologySuite.Features.AttributesTable table = (NetTopologySuite.Features.AttributesTable)feature.Attributes;
string timezone = table["TZID"].ToString();
//set getometry
GeoAPI.Geometries.IGeometry geometry = feature.Geometry;
bool addnew = true;
foreach (GetTimeZones geotimezone in m_geotimezones)
{
//add geometry to existing timezone
if (geotimezone.timezone.CompareTo(timezone) == 0)
{
addnew = false;
geotimezone.geometry = geotimezone.geometry.Concat<GeoAPI.Geometries.IGeometry>(new[] { geometry }).ToArray();
}
}
//add a new timezone and his first geometry
if (addnew)
{
GetTimeZones addgeotimezone = new GetTimeZones();
addgeotimezone.timezone = timezone;
addgeotimezone.geometry = new[] { geometry };
m_geotimezones.Add(addgeotimezone);
}
}
catch { }
}
}
catch { }
}
Then the function that finds if a point, given by latitude / longitude, it also search for the points at a given distance it the exact point is not found, this is usefull for location next to the sea.
public string GetIanaTZName(double latitude, double longitude)
{
string ret = "";
//make search point
GeoAPI.Geometries.Coordinate point = new GeoAPI.Geometries.Coordinate(longitude, latitude);
//loop through zones and search for a point insize the zone
foreach (GetTimeZones geotimezone in m_geotimezones)
{
foreach (GeoAPI.Geometries.IGeometry geometry in geotimezone.geometry)
{
try
{
if(NetTopologySuite.Algorithm.Locate.SimplePointInAreaLocator.Locate(point, geometry).Equals(GeoAPI.Geometries.Location.Interior))
{
ret = geotimezone.timezone;
return ret;
}
}
catch { }
}
}
double distance = 0;
//set search distance for unfound point
distance = 0.01;
if (ret == "")
{
foreach (GetTimeZones geotimezone in m_geotimezones)
{
foreach (GeoAPI.Geometries.IGeometry geometry in geotimezone.geometry)
{
try
{
NetTopologySuite.Algorithm.Distance.PointPairDistance ptdist = new NetTopologySuite.Algorithm.Distance.PointPairDistance();
NetTopologySuite.Algorithm.Distance.DistanceToPoint.ComputeDistance(geometry, point, ptdist);
if (ptdist.Distance < distance)
{
ret = geotimezone.timezone;
return ret;
}
}
catch { }
}
}
}
return ret;
}
Changelog
- 1.0.0.2: added the IanaToWindows Timezone Id converter
- 1.0.0.1: first release
Code
Notes
- read risk disclaimer
- excuse my bad english
Beautiful, concise code!
ReplyDeleteThe solution compiled and run without issue - sadly not always the case these days!
I wondered - is there a way of finding the time offset for a zone? I'm writing a program that produces a sunrise/sunset chart for the year at a given lat/long, but the calculations (aCos out of range) go squify for absolute longitudes > 100. So I've been looking for a solution that provides the time offset. (-11 to +11 hours)
Could you spare a few minutes to help me out?
Hello, you can find an update versione (1.0.0.2), which contains also a Iana to Windows timezone Id. Once you have converted the iana to windows timezone using the Find method from IanaToWindowsTimeZoneMapper class, you can get offset by calling TimeZoneInfo timezone = TimeZoneInfo.FindSystemTimeZoneById(yourwindowsidstring);
Deletevar offset = timezone.BaseUtcOffset;