May 2, 2006 at 6:11 am
This article came just in time. I used to work with address normalization and geocoding with a prior employer that had premium geocoding libraries available (like Group1 Centrus aka Sagent Centrus), so I know what is possible. Now I'm having to do similar work with less resources at a new place, and I'm also trying to give it a SQL 2005 spin. This article hits the mark, and saves me some work. Thank you.
Normally, for this type of service, you want to normalize/standarize the addresses before you geocode them (which fixes the apartment number issue). You can develop your own code to do this, or you can use an address normalization package. For the US and Canada, low cost alternatives include Semaphore Corp ZP4, and ZipInfo Perfect Address.
I found this site http://www.batchgeocode.com that allows you to geocode multiple addresses also using the Yahoo API, if you only need to do a one time run. You can optionally map the first 100 (using Google).
If you don't need high accuracy, need to geocode a large number of addresses quickly, and can live with zip level geocodes, a free alternative is to use the US Census ZCTAs (Zip Code Tabulation Areas). You can download the 2000 ZCTA file, and query it by zip code (or by major Zip Code). Not all zip codes are available, and some zip codes have changed since then, but it gets you pretty close. There is supposed to be a ZCTA 2003 file, but I haven't been able to find it.
May 3, 2006 at 5:07 am
I like the article and the example.
I've tried to install it on my local sql2005STD instance wich has clr enabled and my testdb is trustworthy.
The vb.net version as well as the c#.net version do not work
I did add //Catch any errors here
throw new Exception("mynewexception", ex);
When I execute the url in internet explorer I receive the correct results.
When executed with the sproc it says :
Msg 6522, Level 16, State 1, Procedure spclr_GeocodingC, Line 0
A .NET Framework error occurred during execution of user defined routine or aggregate 'spclr_GeocodingC':
System.Exception: mynewexception---> System.Net.WebException: The remote name could not be resolved: 'api.local.yahoo.com'
System.Net.WebException:
at System.Net.HttpWebRequest.GetResponse()
at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri, ICredentials credentials)
at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials)
at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
at System.Xml.XmlTextReaderImpl.OpenUrlDelegate(Object xmlResolver)
at System.Threading.CompressedStack.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.CompressedStack.Run(CompressedStack compressedStack, ContextCallback callback, Object state)
at System.Xml.XmlTextReaderImpl.OpenUrl()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlTextReader.Read()
at System.Xml.XmlReader.MoveToContent()
at System.Data.DataSet.ReadXml(XmlReader reader, Boolean denyResolving)
at System.Data.DataSet.ReadXml(XmlReader reader)
at StoredProcedures.spclr_GeocodingC(String Address, String City, String State, String appid)
System.Exception:
at StoredProcedures.spclr_GeocodingC(String Address, String City, String State, String appid)
.
This server is running with the same account I am logged in
Any hints ?
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
May 3, 2006 at 6:11 am
I haven't seen this before & I'm somewhat confused. The error would indicate that you can't resolve the URL, so maybe a DNS issue upstream from you. But then you said that it works in IE, which means your machine is indeed resolving the URL. Do you have any firewalls turned on on your local PC? Or anything that might be blocking http traffic coming from things other than IE? Not sure.......Could you try pulling some of the Yahoo API call out into a Windows form, and go into debug and watch as you step through?
May 3, 2006 at 6:46 am
Here it is ... my vbform :
form fields : streettxt , citytxt, statetxt , datagridview Resultsdgv
form button : CallWebServicebtn
here's the code :
Imports
System
Imports
System.Data
'Imports System.Data.SqlClient
'Imports System.Data.SqlTypes
'Imports Microsoft.SqlServer.Server
Imports
System.Net
Imports
System.Xml
Imports
System.Xml.XPath
Imports
System.Text
Public
Class FormStartup
Private Sub CallWebServicebtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CallWebServicebtn.Click
Dim ds As New DataSet
Try
ds = SPCLR_Geocode(Streettxt.Text.ToString, Citytxt.Text.ToString, Statetxt.Text.ToString,
"myyahootestappid")
If ds.Tables.Count > 0 Then
Resultdgv.DataSource = ds.Tables(0)
Else
Resultdgv.DataSource =
Nothing
End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Public Function SPCLR_Geocode(ByVal Address As String, ByVal City As String, ByVal State As String, ByVal appid As String) As DataSet
'Declare string for URL
Dim URL As String = "_empty_"
'Yahoo test appid "myyahootestappid"
Try
'Replace the spaces in your Address with the plus sign to build the URL needed for Yahoo
Address = Address.Replace(
" ", "+")
'Build the URL
URL =
"http://api.local.yahoo.com/MapsService/V1/geocode?appid=" + appid + "&street=" + Address + "&city=" + City + "&state=" + State
'
'Get the XML response
Dim xmlread As New XmlTextReader(URL)
' Create the record and specify the metadata for the columns.
'Dim record As New SqlDataRecord(New SqlMetaData("Latitude", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("Longitude", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("GeoCodedAddress", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("GeoCodedCity", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("GeoCodedState", System.Data.SqlDbType.VarChar, 2), New SqlMetaData("GeoCodedZip", System.Data.SqlDbType.VarChar, 10), New SqlMetaData("GeoCodedCountry", System.Data.SqlDbType.VarChar, 10), New SqlMetaData("Precision", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("Warning", System.Data.SqlDbType.VarChar, 100))
'Create a Dataset to hold the results
Dim ds As New DataSet()
'Fill the DS
ds.ReadXml(xmlread)
' Mark the begining of the result-set.
' SqlContext.Pipe.SendResultsStart(record)
'If the response included an error, then the array length is different, 0-7
'If ds.Tables(0).Rows(0).ItemArray.Length.Equals(9) Then
' record.SetString(0, ds.Tables(0).Rows(0).ItemArray(0).ToString())
' record.SetString(1, ds.Tables(0).Rows(0).ItemArray(1).ToString())
' record.SetString(2, ds.Tables(0).Rows(0).ItemArray(2).ToString())
' record.SetString(3, ds.Tables(0).Rows(0).ItemArray(3).ToString())
' record.SetString(4, ds.Tables(0).Rows(0).ItemArray(4).ToString())
' record.SetString(5, ds.Tables(0).Rows(0).ItemArray(5).ToString())
' record.SetString(6, ds.Tables(0).Rows(0).ItemArray(6).ToString())
' record.SetString(7, ds.Tables(0).Rows(0).ItemArray(7).ToString())
' record.SetString(8, ds.Tables(0).Rows(0).ItemArray(8).ToString())
' 'If the response did not include an error, then the array length is 0-6
'Else
' record.SetString(0, ds.Tables(0).Rows(0).ItemArray(0).ToString())
' record.SetString(1, ds.Tables(0).Rows(0).ItemArray(1).ToString())
' record.SetString(2, ds.Tables(0).Rows(0).ItemArray(2).ToString())
' record.SetString(3, ds.Tables(0).Rows(0).ItemArray(3).ToString())
' record.SetString(4, ds.Tables(0).Rows(0).ItemArray(4).ToString())
' record.SetString(5, ds.Tables(0).Rows(0).ItemArray(5).ToString())
' record.SetString(6, ds.Tables(0).Rows(0).ItemArray(6).ToString())
' record.SetString(7, "Precision Good")
' record.SetString(8, "No Error")
'End If
'SqlContext.Pipe.SendResultsRow(record)
' Mark the end of the result-set.
'SqlContext.Pipe.SendResultsEnd()
Return ds
Catch ex As Exception
Throw New Exception(String.Format("AT_Error with url [{0}]", URL.ToString), ex)
End Try
End Function
End
Class
it also gives an error, but for some proxi thing ???
System.Net.WebException: The remote server returned an error: (407) Proxy Authentication Required.
at System.Net.HttpWebRequest.GetResponse()
at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri, ICredentials credentials)
at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials)
at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
at System.Xml.XmlTextReaderImpl.OpenUrlDelegate(Object xmlResolver)
at System.Threading.CompressedStack.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.CompressedStack.Run(CompressedStack compressedStack, ContextCallback callback, Object state)
at System.Xml.XmlTextReaderImpl.OpenUrl()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlTextReader.Read()
at System.Xml.XmlReader.MoveToContent()
at System.Data.DataSet.ReadXml(XmlReader reader, Boolean denyResolving)
at System.Data.DataSet.ReadXml(XmlReader reader)
at GeocodingWIN.FormStartup.SPCLR_Geocode(String Address, String City, String State, String appid) in P:\Documents\Visual Studio 2005\Projects\Geocoding\GeocodingWIN\FormStartup.vb:line 56"
Being a dba that's trying to code a bit ... HEEELLLLPP
http://api.local.yahoo.com/MapsService/V1/geocode?appid=mytestcode&street=8&city=Seattle&state=WA
once again, internet exporer with the same url works fine
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
May 3, 2006 at 6:53 am
Check in IE - go to Tools, Options, Connections tab, Lan Settings button..., and see if you have a proxy server specified. My guess is that you do. You may need to specify the same proxy server settings for this outgoing request from .NET. I did a quick search and found these....
http://www.thescripts.com/forum/thread425824.html
http://www.codeproject.com/vb/net/web_service_by_proxy.asp?df=100&forumid=16041&exp=0&select=1119964
Let us know how it goes.
May 3, 2006 at 7:52 am
Thank you for your time.
It took me a while, but finaly I got the winform app to work ...
Address = Address.Replace(
" ", "+")
Dim myWebSvc As New System.Net.WebClient
Dim myWebProxy As New System.Net.WebProxy("xxx.yyy.zzz.aaa", myport)
'use current users credentials
myWebProxy.Credentials = System.Net.CredentialCache.DefaultCredentials
myWebSvc.Proxy = myWebProxy
'Build the URL
URL = "http://api.local.yahoo.com/MapsService/V1/geocode?appid=" + appid + "&street=" + Address + "&city=" + City + "&state=" + State
'Get the XML response
Dim xmlread As New XmlTextReader(myWebSvc.OpenRead(URL))
Now, can I just port this code into the CLR-proc for sql2005 ?
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
May 3, 2006 at 7:54 am
Theoretically.....we'll see. Give it a try.
May 3, 2006 at 8:08 am
It works just fine now !!
Imports
System
Imports
System.Data
Imports
System.Data.SqlClient
Imports
System.Data.SqlTypes
Imports
Microsoft.SqlServer.Server
Imports
System.Net
Imports
System.Xml
Imports
System.Xml.XPath
Imports
System.Text
'Zie http://www.sqlservercentral.com/columnists/cBunch/geocodingwithsqlserver2005_printversion.asp
Partial
Public Class StoredProcedures
<Microsoft.SqlServer.Server.SqlProcedure()> _
Public Shared Sub SPCLR_Geocode(ByVal Address As String, ByVal City As String, ByVal State As String, ByVal appid As String)
'Declare string for URL
Dim URL As String = "_empty_"
'Yahoo test appid ""
Try
'Replace the spaces in your Address with the plus sign to build the URL needed for Yahoo
Address = Address.Replace(
" ", "+")
'Provide webservice and proxy and credentials
Dim myWebSvc As New System.Net.WebClient
Dim myWebProxy As New System.Net.WebProxy("xxx.yyy.zzz.aaa", myport)
'use current users credentials
myWebProxy.Credentials = System.Net.CredentialCache.DefaultCredentials
myWebSvc.Proxy = myWebProxy
'Build the URL
URL =
"http://api.local.yahoo.com/MapsService/V1/geocode?appid=" + appid + "&street=" + Address + "&city=" + City + "&state=" + State
' http://api.local.yahoo.com/MapsService/V1/geocode?appid=myyahooappid&street=8&city=seattle&state=wa
'
'Get the XML response
Dim xmlread As New XmlTextReader(myWebSvc.OpenRead(URL))
' Create the record and specify the metadata for the columns.
Dim record As New SqlDataRecord(New SqlMetaData("Latitude", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("Longitude", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("GeoCodedAddress", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("GeoCodedCity", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("GeoCodedState", System.Data.SqlDbType.VarChar, 2), New SqlMetaData("GeoCodedZip", System.Data.SqlDbType.VarChar, 10), New SqlMetaData("GeoCodedCountry", System.Data.SqlDbType.VarChar, 10), New SqlMetaData("Precision", System.Data.SqlDbType.VarChar, 100), New SqlMetaData("Warning", System.Data.SqlDbType.VarChar, 100))
'Create a Dataset to hold the results
Dim ds As New DataSet()
'Fill the DS
ds.ReadXml(xmlread)
' Mark the begining of the result-set.
SqlContext.Pipe.SendResultsStart(record)
For Each Rij As DataRow In ds.Tables(0).Rows
If Rij.ItemArray.Length.Equals(9) Then
record.SetString(0, Rij.ItemArray(0).ToString())
record.SetString(1, Rij.ItemArray(1).ToString())
record.SetString(2, Rij.ItemArray(2).ToString())
record.SetString(3, Rij.ItemArray(3).ToString())
record.SetString(4, Rij.ItemArray(4).ToString())
record.SetString(5, Rij.ItemArray(5).ToString())
record.SetString(6, Rij.ItemArray(6).ToString())
record.SetString(7, Rij.ItemArray(7).ToString())
record.SetString(8, Rij.ItemArray(8).ToString())
'If the response did not include an error, then the array length is 0-6
Else
record.SetString(0, Rij.ItemArray(0).ToString())
record.SetString(1, Rij.ItemArray(1).ToString())
record.SetString(2, Rij.ItemArray(2).ToString())
record.SetString(3, Rij.ItemArray(3).ToString())
record.SetString(4, Rij.ItemArray(4).ToString())
record.SetString(5, Rij.ItemArray(5).ToString())
record.SetString(6, Rij.ItemArray(6).ToString())
record.SetString(7,
"Precision Good")
record.SetString(8,
"No Error")
End If
SqlContext.Pipe.SendResultsRow(record)
Next
' Mark the end of the result-set.
SqlContext.Pipe.SendResultsEnd()
Catch ex As Exception
Throw New Exception(String.Format("AT_Error with url [{0}]", URL.ToString), ex)
End Try
End Sub
End
Class
Deploy and
EXECUTE SPCLR_Geocode '8','Seattle','WA','MyYahooAppId'
Thank your for your support !
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
May 10, 2006 at 4:13 pm
I recoded it as a TVF function. I modified the logic to return multiple matches, and did some code changes.
I used it to geocode 500 addresses, and it found 4 that returned multiple matches (3 of them at the address level). While it doesn't happen often, and the accuracy of the first match is probably good enough for most applications, if you want high accuracy, you should analyze all the matches returned.
Some examples of how to call it (the first 3 return multiple matches)
select * from dbo.fncGeocodeByAddress ( '245 Union Ave', 'Pueblo', 'CO', '81003', 'YahooDemo' )
select * from dbo.fncGeocodeByAddress ( '7920 Stone Creek Dr.', 'Chanhassen', 'MN', '55317', 'YahooDemo' )
select * from dbo.fncGeocodeByLocation ( 'Springfield', 'YahooDemo' )
select * from dbo.fncGeocodeByAddress ( '100 East 42nd Street', 'new york', 'NY', Null, 'YahooDemo' )
select * from dbo.fncGeocodeByAddress ( '701 First Street', 'Sunnyvale', 'CA', Null, 'YahooDemo' )
By making it a TVF, it is more flexible, and you can call it in a select using CROSS APPLY
insert tmpAddressGeocode
(
[AddressGuid]
,[Latitude]
,[Longitude]
,[Address]
,[City]
,[State]
,[Zip]
,[Country]
,[Precision]
,[Warning]
)
select top 100
addr.AddressGuid,
gc.*
from
dbo.AddressTbl addr with (NoLock)
left join tmpAddressGeocode alldone with (NoLock)
on alldone.AddressGuid = addr.AddressGuid
cross apply dbo.fncGeocodeByAddress (
addr.StreetAddress1 + coalesce (' ' + StreetAddress2,''),
addr.CityName,
addr.State,
addr.Zip,
'YahooDemo' ) gc
where
alldone.AddressGuid is Null
SQL Installation
CREATE FUNCTION [dbo].[fncGeocodeByLocation](@Location nvarchar(4000), @appid nvarchar(100))
RETURNS TABLE (
[Latitude] float Null,
[Longitude] float Null,
[Address] nvarchar(80) Null,
[City] nvarchar(35) Null,
[State] nchar(2) Null,
[Zip] nvarchar(10) Null,
[Country] nvarchar(35) Null,
[Precision] nvarchar(10) Null,
[Warning] nvarchar(1024) Null
)
AS EXTERNAL NAME [YahooGCSQL].[sqlservercentral.Geocoding.YGeocode].[GeocodeByLocation]
GO
CREATE FUNCTION [dbo].[fncGeocodeByAddress](
@Address [nvarchar](4000),
@City [nvarchar](4000),
@State [nvarchar](4000),
@Zip [nvarchar](10),
@appid nvarchar(100)
)
RETURNS TABLE (
[Latitude] float Null,
[Longitude] float Null,
[Address] nvarchar(80) Null,
[City] nvarchar(35) Null,
[State] nchar(2) Null,
[Zip] nvarchar(10) Null,
[Country] nvarchar(35) Null,
[Precision] nvarchar(10) Null,
[Warning] nvarchar(1024) Null
)
AS EXTERNAL NAME [YahooGCSQL].[sqlservercentral.Geocoding.YGeocode].[GeocodeByAddress]
GO
Function Assembly Code
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using System.Text;
using Microsoft.SqlServer.Server;
{
public sealed class YGeocode
{
/// <summary>
/// Prevent instances by making constructor private
/// </summary>
private YGeocode()
{
}
/// Query Yahoo Geocode API, and return an enumeration of results. Have to provide City and State or Zip or Location. Data is extracted for SQL by FillRow
/// </summary>
/// <param name="AppId">Yahoo application ID</param>
/// <param name="StreetAddress">Street Address</param>
/// <param name="City">City name</param>
/// <param name="State">US State 2 letter abbreviation</param>
/// <param name="Zip">Zip Code</param>
/// <param name="Location">Free form address/location</param>
/// <returns>Enumeration of addresses found</returns>
private static IEnumerable GeocodeStart(SqlString AppId, SqlString StreetAddress, SqlString City, SqlString State, SqlString Zip, SqlString Location)
{
// Based on
http://www.sqlservercentral.com/columnists/cBunch/geocodingwithsqlserver2005.asptry
{
// Build the URL
StringBuilder sb = new StringBuilder();
sb.Append(AppId);
if (!StreetAddress.IsNull)
{
sb.Append("&street=");
sb.Append(StreetAddress);
}
if (!City.IsNull)
{
sb.Append("&city=");
sb.Append(City);
}
if (!State.IsNull)
{
sb.Append("&state=");
sb.Append(State);
}
if (!Zip.IsNull)
{
sb.Append("&zip=");
sb.Append(Zip);
}
if (!Location.IsNull)
{
sb.Append("&location=");
sb.Append(Location);
}
// Replace spaces and # with +
// string Address = System.Web.HttpUtility.UrlEncode(StreetAddress);
// It seems that HttpUtility.UrlEncode is not available here (Compact Framework?), so we have to do it "manually"
// If need be, could use the implementation available at http://www.devx.com/vb2themax/Tip/19352 (just check for the = and & )
sb.Replace('#', '+');
sb.Replace(' ', '+');
// Prepend the API URL
sb.Insert(0, "http://api.local.yahoo.com/MapsService/V1/geocode?appid=");
XmlTextReader xmlread = new XmlTextReader(URL);
DataSet ds = new DataSet();
ds.ReadXml(xmlread);
return ds.Tables[0].Rows;
}
catch (Exception ex)
{
// Catch any errors here
;
}
return null;
}
TableDefinition="[Latitude] float Null, [Longitude] float Null, [Address] nvarchar(80) Null, [City] nvarchar(35) Null, [State] nchar(2) Null, [Zip] nvarchar(10) Null, [Country] nvarchar(35) Null, [Precision] nvarchar(15) Null, [Warning] nvarchar(1024) Null")]
public static IEnumerable GeocodeByAddress(SqlString StreetAddress, SqlString City, SqlString State, SqlString Zip, SqlString AppId)
{
return GeocodeStart(AppId, StreetAddress, City, State, Zip, null);
}
TableDefinition = "[Latitude] float Null, [Longitude] float Null, [Address] nvarchar(80) Null, [City] nvarchar(35) Null, [State] nchar(2) Null, [Zip] nvarchar(10) Null, [Country] nvarchar(35) Null, [Precision] nvarchar(15) Null, [Warning] nvarchar(1024) Null")]
public static IEnumerable GeocodeByLocation(SqlString Location, SqlString AppId)
{
return GeocodeStart(AppId, null, null, null, null, Location);
}
public static void FillRow(object obj,
out SqlDouble Latitude, out SqlDouble Longitude,
out string Address, out string City, out string State, out string Zip, out string Country,
out string Precision, out string Warning )
{
// XML elements and attributes are case sensitive.
// Access by name to protect against API changes. It is slower, but not so bad compared with the cost of calling an Internet API
DataRow row = (DataRow)obj;
if (!String.IsNullOrEmpty(row["Latitude"].ToString()))
Latitude = Convert.ToDouble(row["Latitude"].ToString());
else
Latitude = SqlDouble.Null;
if (!String.IsNullOrEmpty(row["Longitude"].ToString()))
Longitude = Convert.ToDouble(row["Longitude"].ToString());
else
Longitude = SqlDouble.Null;
Address = (!String.IsNullOrEmpty(row["Address"].ToString())) ? row["Address"].ToString() : null;
City = (!String.IsNullOrEmpty(row["City"].ToString())) ? row["City"].ToString() : null;
State = (!String.IsNullOrEmpty(row["State"].ToString())) ? row["State"].ToString() : null;
Zip = (!String.IsNullOrEmpty(row["Zip"].ToString())) ? row["Zip"].ToString() : null;
Country = (!String.IsNullOrEmpty(row["Country"].ToString())) ? row["Country"].ToString() : null;
// Better way to detect precision/warning columns w/o going back up to the columns collection? w/o resorting to exception trapping? w/o assuming indexes?
// Alternate way = Precision = (row.ItemArray.Length >= 8) && !String.IsNullOrEmpty(row["precision"].ToString()) ? row["precision"].ToString() : null;
Precision = (row.Table.Columns.Contains("precision")) && !String.IsNullOrEmpty(row["precision"].ToString()) ? row["precision"].ToString() : null;
Warning = (row.Table.Columns.Contains("warning")) && !String.IsNullOrEmpty(row["warning"].ToString()) ? row["warning"].ToString() : null;
}
}
};
May 11, 2006 at 4:37 pm
Great addition! Thanks!
February 8, 2007 at 9:19 pm
I've just found this tutorial today, and it's awesome! I've contacted Corey directly, but I'm also throwing this one out there for anyone that might catch it. My DBA is out of town, and if I wanted to execute the Assembly and immediately stick the lat/lon into a table from within an Insert trigger for a newly created row, what would that look like?
February 9, 2007 at 12:16 am
- it would look like bad code !
- keep in mind you have no control over the availability of the counterparty !
- If you put this clr-routine into a trigger, it will execute "in transaction", meaning if the clr-routine fails, your transaction will fail, so your actual insert statement will be rolled back !!!
- You might want to put this in an assynchrone process (e.g. a sqlagent job).
maybe within the insert trigger just feedin the pk into a "trigger"-table
on which your sqlagent job will start from to get all the geo-info
and will report bad info.
Johan
Learn to play, play to learn !
Dont drive faster than your guardian angel can fly ...
but keeping both feet on the ground wont get you anywhere :w00t:
- How to post Performance Problems
- How to post data/code to get the best help[/url]
- How to prevent a sore throat after hours of presenting ppt
press F1 for solution, press shift+F1 for urgent solution ๐
Need a bit of Powershell? How about this
Who am I ? Sometimes this is me but most of the time this is me
February 9, 2007 at 6:59 am
alzdba - This is exactly what we do on our production system. We have another table where a bit gets set on or off based on business rules, and then a sql job comes & picks up the bits=1 and runs the CLR process. This is what I would recommend as well.
February 9, 2007 at 12:37 pm
I also agree that making a CLR call from within a Trigger is a bit risky (your DBA probably wonโt appreciate you doing this while he is away). With this assembly, I've encountered a sporadic error, presumably due to connectivity issues, that locks the assembly, and forces you to have to reinstall it. I haven't been able to reproduce it in a controlled environment to try and handle it within the assembly.
Msg 6260, Level 16, State 1, Line 1
An error occurred while getting new row from user defined Table Valued Function :
System.InvalidOperationException: Handle is not initialized.
System.InvalidOperationException:
at System.Runtime.InteropServices.GCHandle.FromIntPtr(IntPtr value)
.
The statement has been terminated.
That aside, to handle multiple matches by averaging the geocodes, using the TSV function above, the code would look like
select
addr.AddressGuid,
gc.*
into #tmpAddressGeocode
from
dbo.AddressTbl addr with (NoLock)
cross apply dbo.fncGeocodeByAddress (
addr.StreetAddress1 + coalesce (' ' + StreetAddress2,''),
addr.CityName,
addr.State,
addr.Zip,
'YahooDemo' ) gc
update addr set
LatitudeRad = LatAvg
, LongitudeRad = LongAvg
, LatestGeocodeAttemptDate = getutcdate()
, GeocodePrecision = Prec
, GeocodeMultiCnt = GeoCnt
from (
select
AddressGuid,
LatMin = Min (LatitudeRad),
LongMin = Min (LongitudeRad),
LatMax = Max (LatitudeRad),
LongMax = Max (LongitudeRad),
LatAvg = Avg (LatitudeRad),
LongAvg = Avg (LongitudeRad),
Prec = dbo.Concatenate (distinct geos.[Precision] ),
GeoCnt = count (*)
from #tmpAddressGeocode geos with (NoLock)
group by AddressGuid
) geos
join dbo.AddressTbl addr on addr.AddressGuid = geos.AddressGuid
where
-- 5 miles or less when multiple points
dbo.DistanceGreatArc ( LatMin, LongMin, LatMax, LongMax ) * dbo.EarthRadiusCalc ( 1, null ) <= 10.0
drop table #tmpAddressGeocode
Iโm storing other information, like when the geocode was obtained, a list of the precision levels, and how many matches were found. I'm also using a couple other CLR functions. dbo.Concatenate is included in the SQL CLR programming samples, and dbo.DistanceGreatArc is used to calculate the distance (there are multiple implementations you can find on the Internet, either as a CLR or SQL function). The check for the radius to be within 5 miles is a bit redundant, and you could omit it.
If youโre geocoding a considerable number of records, you probably want to mange the temp table a bit more, and use a top to limit the number of geocodes you perform on each run (see earlier post).
February 21, 2007 at 9:04 am
I have followed the tutorial and everything went according to plan but when i execute the SP, it says Command Completed Successfully but I dont get any rows returned. I'm using the same test data as in the tutorial and I've tried other addresses too.
Any ideas whats wrong or where I should start troubleshooting?
Thanks
Viewing 15 posts - 16 through 30 (of 58 total)
You must be logged in to reply to this topic. Login to reply