业务区域浏览器 - 一个新的商业机会






4.80/5 (2投票s)
正在寻找一个结合邓白氏公司数据、ESRI人口统计信息和谷歌地图的商业机会
介绍
想象一下,您想在美国创办一家企业,但对最适合开展此类业务的地区一无所知,也不知道该地区的竞争对手和人口统计信息。您应该去哪里?——商业领域探索器是您的理想起点!
应用程序可在 http://businessareaexplorerv2.cloudapp.net/ 访问。
商业案例
案例 1
一家风险投资公司希望投资“绿色”公司,或根据特定地区的人口统计信息(如少数族裔、退伍军人等相关行业的拥有情况)在特定地区创办“绿色”公司。VC 需要能够根据标准过滤数据,并找到人口密度高的/低的地区(按邮政编码、县等)。“热力图”可以描绘人口密度高的/低的地区,并叠加在过滤后的数据之上,以 pinpoint 投资重点区域。
案例2
一家医疗保险公司正在组织一项营销活动,以确定高/低人口区域,目标是拥有特定净值和公司规模的女性、退伍军人、少数族裔拥有的公司。根据政府的医疗改革法案,此次活动将用于吸引这些公司的员工加入2014年可供这些员工使用的私人医疗保险交易所 (HIX)。
实现故事
长话短说,我们想将公司数据与人口统计数据进行整合,以便进行交叉过滤,例如:显示拥有10名以上员工、净值超过100万美元、并且位于年资本收入增长超过5%的地区的公司。
整合最重要的东西是什么?技术?不,是数据。
获取数据
Windows Azure Marketplace 提供了一个简便的方法来订阅不同的数据集并管理这些订阅。
我们将需要公司数据,这些数据可以从邓白氏开发者沙盒提要中提取 https://datamarket.azure.com/dataset/dnb/developersandbox 。
我们还将使用人口统计数据,这些数据可以从 ESRI 数据提要中获得 https://datamarket.azure.com/dataset/esri/keyusdemographicstrial
从客户的角度来看,使用数据市场提要并没有太大区别。您通过指向提要 URI 的数据生成代理,例如 https://api.datamarket.azure.com/DNB/DeveloperSandbox/v1。
为了使其正常工作,您需要为生成的代理设置凭据,将 LiveID 设置为用户名,将主账户密钥设置为密码。在 .Net 中,它将如下所示
proxy.Credentials = new NetworkCredential("live id","primary account key");
进行整合
由于我们要进行交叉过滤,我们需要以某种方式整合这些数据集。
邓白氏数据可以通过 DUNSNumber 连接 - 这是邓白氏生成和维护的公司标识符。
为了整合邓白氏公司数据与 ESRI 数据,我们应该从 LocationLatLong 邓白氏数据集中获取 ZipCode 数据,并将其与 ESRI 的 demog1 数据集中的 GeographyId 字段进行匹配。
您将获取 GeographyName 等于 Zip Code 的所有 ESRI 数据,然后逐一查找 LocationLatLong 公司,其 ZipCode 以 GeographyId 的值为开头。
在整合了数据集之后,您就可以根据人口统计和公司属性过滤数据了。
构建应用程序
我们希望我们的应用程序易于访问,因此我们没有选择 Win8/Windows 应用商店应用程序,而是从“老式”HTML5 开始。一旦您使用 HTML5,您就不可避免地会使用 jquery 和 jqgrid。
想在地图上显示内容?谷歌地图是主流技术。
ASP.NET MVC4 也很适合。
最后一件事——我们将如何托管它?云计算前来救援——我们可以通过以下方式将我们的应用程序发布到 Windows Azure:
在 Visual Studio 中只需点击“发布”。
不到两天时间,我们的整合项目就上线了。没有什么比刚完成的工作更能令人欣慰的了。
你能想象一篇没有代码的 codeproject 文章吗?
让我们用一些代码充实这篇文章。我想向您展示我们如何将公司数据通过应用程序从邓白氏后端传输到 HTML5 UI。
让我们假设我们已经成功为邓白氏提要生成了一个代理。现在我们将它包装在我们自己的数据访问层中。
首先,我们将定义一个 Company 类,其中包含我们将要使用的所有属性。
public sealed class Company { public string DUNSNumber { get; set; } public string CompanyName { get; set; } public int NumberOfEmployees { get; set; } public decimal NetWorth { get; set; } public string Industry { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } public string Country { get; set; } public string Green { get; set; } public string WomanOwned { get; set; } public string VeteranOwned { get; set; } public string MinorityOwned { get; set; } public decimal Latitude { get; set; } public decimal Longtitude { get; set; } }
我们还需要应用过滤,因此我们需要定义一个包含过滤条件的请求类。
public sealed class CompanyRequest { public CompanyRequest() { MaxEmployees = int.MaxValue; MaxNetWorth = decimal.MaxValue; } public LatLong NE { get; set; } public LatLong SW { get; set; } public int MinEmployees { get; set; } public int MaxEmployees { get; set; } public decimal MinNetWorth { get; set; } public decimal MaxNetWorth { get; set; } public string IndustryCode { get; set; } public bool Green { get; set; } public bool WomanOwned { get; set; } public bool VeteranOwned { get; set; } public bool MinorityOwned { get; set; } public List<string> GeoIds { get; set; } }
我们还需要一个小的 LatLong 类来表示坐标。
public sealed class LatLong { public decimal Latitude { get; set; } public decimal Longitude { get; set; } }
现在,让我们转向数据源实现。为了简洁起见,我删除了大部分数据查询代码。
public class CompanyDataSource { public List<Company> GetCompanies(CompanyRequest request) { if(request == null) throw new ArgumentNullException("request"); var companies = FetchCompanies(); companies = ApplyFilters(request, companies); return companies; } protected virtual List<Company> FetchCompanies() { DNBDeveloperSandboxContainer proxy = CreateProxy(); DataServiceQuery<LocationLatLong> locations = GetLocationInformation(proxy); DataServiceQuery<PublicRecords> publicRecords = GetPublicRecords(proxy); DataServiceQuery<Firmographics> firmographics = GetFirmographics(proxy); DataServiceQuery<Green> greenCompanies = GetGreenCompanies(proxy); DataServiceQuery<Veteran> veteranOwnedCompanies = GetVeteranOwnedCompanies(proxy); DataServiceQuery<Women> womenOwnedCompanies = GetWomenOwnedCompanies(proxy); DataServiceQuery<Minority> minorityOwnedIndicator = GetMinorityOwnedIndicator(proxy); var locationOperation = Task<IEnumerable<LocationLatLong>>.Factory.FromAsync(locations.BeginExecute(null, null), locations.EndExecute); var publicRecordOperation = Task<IEnumerable<PublicRecords>>.Factory.FromAsync(publicRecords.BeginExecute(null, null), publicRecords.EndExecute); var firmographicsOperation = Task<IEnumerable<Firmographics>>.Factory.FromAsync(firmographics.BeginExecute(null, null), firmographics.EndExecute); firmographicsOperation.Wait(); var greenCompaniesOperation = Task<IEnumerable<Green>>.Factory.FromAsync(greenCompanies.BeginExecute(null, null), greenCompanies.EndExecute); var veteranOwnedOperation = Task<IEnumerable<Veteran>>.Factory.FromAsync(veteranOwnedCompanies.BeginExecute(null, null), veteranOwnedCompanies.EndExecute); var womenOwnedOperation = Task<IEnumerable<Women>>.Factory.FromAsync(womenOwnedCompanies.BeginExecute(null, null), womenOwnedCompanies.EndExecute); var minorityOwnedOperation = Task<IEnumerable<Minority>>.Factory.FromAsync(minorityOwnedIndicator.BeginExecute(null, null), minorityOwnedIndicator.EndExecute); Task.WaitAll(new Task[] { locationOperation, publicRecordOperation, firmographicsOperation, greenCompaniesOperation, veteranOwnedOperation, womenOwnedOperation, minorityOwnedOperation }); List<LocationLatLong> locationsResult = locationOperation.Result.ToList(); List<Company> companies = new List<Company>(locationsResult.Count); AddLocations(locationsResult, companies); AddPublicRecords(companies, publicRecordOperation.Result.ToList()); AddFirmographics(companies, firmographicsOperation.Result.ToList()); AddGreen(companies, greenCompaniesOperation.Result.ToList()); AddVeteranOwned(companies, veteranOwnedOperation.Result.ToList()); AddWomenOwned(companies, womenOwnedOperation.Result.ToList()); AddMinorityOwned(companies, minorityOwnedOperation.Result.ToList()); return companies; } private void AddPublicRecords(IEnumerable<Company> companies, IEnumerable<PublicRecords> publicRecords) { var lookup = publicRecords.ToLookup(m => m.DUNSNumber); foreach (var company in companies) { var publicRecord = lookup[company.DUNSNumber].FirstOrDefault(); if(publicRecord != null) { if(String.IsNullOrEmpty(company.CompanyName)) company.CompanyName = publicRecord.Company; company.Address = publicRecord.Address; company.City = publicRecord.City; company.Country = publicRecord.Country; company.State = publicRecord.StateAbbrv; company.ZipCode = publicRecord.ZipCode; company.NetWorth = ParseDecimal(publicRecord.NetWorth); } } } private void AddLocations(IEnumerable<LocationLatLong> locationsResult, List<Company> companies) { foreach (var location in locationsResult) { var company = new Company(); company.CompanyName = location.Company; company.DUNSNumber = location.DUNSNumber; company.Latitude = ParseDecimal(location.Latitude); company.Longtitude = ParseDecimal(location.Longitude); companies.Add(company); } } private DataServiceQuery<PublicRecords> GetPublicRecords(DNBDeveloperSandboxContainer proxy) { return (DataServiceQuery<PublicRecords>) proxy.PublicRecords.Select(p => new PublicRecords() { Address = p.Address, City = p.City, Company = p.Company, Country = p.Country, StateAbbrv = p.StateAbbrv, DUNSNumber = p.DUNSNumber, ZipCode = p.ZipCode, NetWorth = p.NetWorth }).Take(DataSetSize); } private static DataServiceQuery<LocationLatLong> GetLocationInformation(DNBDeveloperSandboxContainer proxy) { return (DataServiceQuery<LocationLatLong>)proxy.LocationLatLong.Take(10000) .Select( m => new LocationLatLong() {Company = m.Company,DUNSNumber = m.DUNSNumber, Latitude = m.Latitude, Longitude = m.Longitude }).Take(DataSetSize); } private DNBDeveloperSandboxContainer CreateProxy() { DNBDeveloperSandboxContainer proxy = new DNBDeveloperSandboxContainer( new Uri("https://api.datamarket.azure.com/DNB/DeveloperSandbox/v1")); ProxySetup.Setup(proxy); return proxy; } } }
在这里,我们正在异步地从几个邓白氏提要中获取数据,然后将信息组合成一个数据集,然后根据我们需要的条件进行过滤。
我们有一个特殊的 ProxySetup 帮助类,用于将身份验证数据和压缩支持推送到代理。
public static class ProxySetup { public static void Setup(System.Data.Services.Client.DataServiceContext proxy) { if(proxy == null) throw new ArgumentNullException(); string clientKey = ConfigurationManager.AppSettings["ClientKey"]; if (String.IsNullOrEmpty(clientKey)) throw new InvalidOperationException("ClientKey is empty"); string clientSecret = ConfigurationManager.AppSettings["ClientSecret"]; if (String.IsNullOrEmpty(clientSecret)) throw new InvalidOperationException("ClientSecret is empty"); proxy.Credentials = new NetworkCredential(clientKey,clientSecret); proxy.MergeOption = MergeOption.NoTracking; proxy.SendingRequest += SendingRequest; } static void SendingRequest(object sender, SendingRequestEventArgs e) { e.RequestHeaders.Add("Accept-Encoding", "gzip,deflate"); ((HttpWebRequest)e.Request).AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; } }
我们希望缓存数据。为了实现这一点,我们将创建一个围绕数据服务的装饰器。没有黑魔法——使用的是一个老式的 ASP.NET 缓存。
public sealed class CachingCompanyDataSource : CompanyDataSource { protected override List<Company> FetchCompanies() { string key = "COMPANIES"; object companies = GetFromCache(key); if (companies != null) return (List<Company>)companies; var results = base.FetchCompanies(); AddToCache(key,results); return results; } private void AddToCache(string key, object value) { HttpContext.Current.Cache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromDays(1)); } private object GetFromCache(string value) { return HttpContext.Current.Cache[value]; } }
一旦我们有了数据服务,我们就可以在 ASP.NET MVC 控制器中使用它,并在收到请求时将数据返回给客户端。请注意,使用了自定义操作过滤器“CompressFilter”来压缩发送到客户端的 JSON。
public class HomeController : Controller { private readonly CachingCompanyDataSource _companiesDataSource = new CachingCompanyDataSource(); private readonly ESRIDataService _esriDataService = new ESRIDataService(); [HttpPost] [CompressFilter] public ActionResult GetCompanies([ModelBinder(typeof(RequestModelBinder))] CompanyRequest companyRequest, [ModelBinder(typeof(RequestModelBinder))] DemographicsRequest demographicsRequest) { var items = _esriDataService.GetStats(demographicsRequest); List<string> geoIds = items.Select(i => i.GeographyId).Distinct().ToList(); companyRequest.GeoIds = geoIds; var companies = _companiesDataSource.GetCompanies(companyRequest); var result = Json(new {Companies=companies, Items=items}); result.MaxJsonLength = int.MaxValue; return result; } } }
现在,我们应该编写一些 JavaScript 来查询我们的数据源(为了简洁起见,删除了一些函数)。
(function ($, window) { $(function () { var urlPublicRecordsTemplate = $('#mapSection').data('public-records-url'); function reloadData(urlTemplate, startLat, endLat, startLon, endLon, onSuccess, onComplete) { var industry = $('#industry').val(); var minMaxEmployees = getMinMaxEmployees(); var minMaxNetWorth = getMinMaxNetWorth(); var postData = { startLatitude: startLat, startLongitude: startLon, endLatitude: endLat, endLongitude: endLon, recordType: getSelectedRecords(), industryCode: industry, minEmployees: minMaxEmployees.min, maxEmployees: minMaxEmployees.max, minNetWorth: minMaxNetWorth.min, maxNetWorth: minMaxNetWorth.max, minPercentOfUnemployment: getNumber('#minPercentOfUnemployment'), maxPercentOfUnemployment: getNumber('#maxPercentOfUnemployment'), minPerCapitalIncome: getNumber('#minPerCapitalIncome'), maxPerCapitalIncome: getNumber('#maxPerCapitalIncome'), minTotalHouseholds: getNumber('#minTotalHouseholds'), maxTotalHouseholds: getNumber('#maxTotalHouseholds'), minAverageHouseholdSize: getNumber('#minAverageHouseholdSize'), maxAverageHouseholdSize: getNumber('#maxAverageHouseholdSize'), minMedianHomeValue: getNumber('#minMedianHomeValue'), maxMedianHomeValue: getNumber('#maxMedianHomeValue'), minHouseholdGrowth: getNumber('#minHouseholdGrowth'), maxHouseholdGrowth: getNumber('#maxHouseholdGrowth'), minPerCapitaIncomeGrowth: getNumber('#minPerCapitaIncomeGrowth'), maxPerCapitaIncomeGrowth: getNumber('#maxPerCapitaIncomeGrowth') }; $.post(urlTemplate, postData). done(onSuccess). fail(function(e1, e2, e3) { alert('An error occured during the data loading.'); }). always(onComplete); } function reload(startLat, endLat, startLon, endLon, mapShouldBeReloaded) { $.screenBlocker.blockScreen(); reloadData(urlPublicRecordsTemplate, startLat, endLat, startLon, endLon, function (result) { if (mapShouldBeReloaded) { loadMap(result.Companies); } loadCompaniesGrid(result.Companies); loadDemographicGrid(result.Items); }, function() { $.screenBlocker.unblockScreen(); }); } }); }(jQuery, window));
希望您能从这篇文章中获得一些有用的信息。保重,并编写代码。
您诚挚的,
Aliaksandr Pedzko。