public static IDictionary <object, IDictionary <int, IDictionary <int, IList <int> > > > GetResolutionsByIndicator() { // First get the timeframes and make a dictionary for fast lookups. IDictionary <object, NycTimeframe> timesById = new Dictionary <object, NycTimeframe>(); foreach (NycTimeframe time in _timeDao.Get()) { timesById[time.UID] = time; } // Now get all the times available for every indicator at every resolution, and // organize them. Start with dictionaries of years so we can easily avoid dupes. IDictionary <object, IDictionary <NycResolutionType, IDictionary <NycTimeframeType, IDictionary <int, int> > > > dictionaryPile = new Dictionary <object, IDictionary <NycResolutionType, IDictionary <NycTimeframeType, IDictionary <int, int> > > >(); foreach (NycResolutionForIndicator res in _resolutionDao.Get()) { IDictionary <NycResolutionType, IDictionary <NycTimeframeType, IDictionary <int, int> > > resForInd; if (dictionaryPile.ContainsKey(res.IndicatorId)) { resForInd = dictionaryPile[res.IndicatorId]; } else { resForInd = new Dictionary <NycResolutionType, IDictionary <NycTimeframeType, IDictionary <int, int> > >(); dictionaryPile[res.IndicatorId] = resForInd; } IDictionary <NycTimeframeType, IDictionary <int, int> > yearsByTimeType; if (resForInd.ContainsKey(res.Resolution)) { yearsByTimeType = resForInd[res.Resolution]; } else { yearsByTimeType = new Dictionary <NycTimeframeType, IDictionary <int, int> >(); resForInd[res.Resolution] = yearsByTimeType; } // This check is just to protect us from bad data, a time being claimed to be // supported in the indicator time list but not actually existing. if (timesById.ContainsKey(res.TimeId)) { NycTimeframe time = timesById[res.TimeId]; IDictionary <int, int> years; if (yearsByTimeType.ContainsKey(time.Type)) { years = yearsByTimeType[time.Type]; } else { years = new Dictionary <int, int>(); yearsByTimeType[time.Type] = years; } years[time.Year] = time.Year; } } // Now convert those dictionaries to lists and sort 'em. IDictionary <object, IDictionary <int, IDictionary <int, IList <int> > > > retVal = new Dictionary <object, IDictionary <int, IDictionary <int, IList <int> > > >(); foreach (KeyValuePair <object, IDictionary <NycResolutionType, IDictionary <NycTimeframeType, IDictionary <int, int> > > > dict1 in dictionaryPile) { IDictionary <int, IDictionary <int, IList <int> > > list1 = new Dictionary <int, IDictionary <int, IList <int> > >(); foreach (KeyValuePair <NycResolutionType, IDictionary <NycTimeframeType, IDictionary <int, int> > > dict2 in dict1.Value) { IDictionary <int, IList <int> > list2 = new Dictionary <int, IList <int> >(); foreach (KeyValuePair <NycTimeframeType, IDictionary <int, int> > dict3 in dict2.Value) { List <int> list3 = new List <int>(dict3.Value.Keys); list3.Sort(); list2[(int)dict3.Key] = list3; } list1[(int)dict2.Key] = list2; } retVal[dict1.Key] = list1; } return(retVal); }
/// <summary> /// Queries for Nychanis data for the specified year range, resolution, and indicator. /// </summary> /// <param name="indicatorId">ID of the indicator being queried.</param> /// <param name="resolutionType">What resolution are we querying for.</param> /// <param name="timeUnitType">What type of time units should the data be in?</param> /// <param name="startYear">First year to include in the results.</param> /// <param name="endYear">Last year to include in the results.</param> /// <param name="scopeSubborough"></param> /// <param name="scopeBorough"></param> /// <param name="orderCol">The column to sort by.</param> /// <param name="orderDir">The direction to sort, ignored if order is less than zero. /// <param name="numPerPage">Number of records to be returned in the page sequence, if not less than zero</param> /// <param name="page">Which page in this page sequence is this request for. /// If null, assumed to be ascending.</param> /// <returns>The results!</returns> public static NycResultsWithMetadata Query(object indicatorId, NycResolutionType resolutionType, NycTimeframeType timeUnitType, int startYear, int endYear, object scopeBorough, object scopeSubborough, int orderCol, SortType?orderDir, int numPerPage, int page) { NycResultsWithMetadata retVal = new NycResultsWithMetadata(); // First load the indicator metadata, both because we need the display name, and because // if the query is for data that doesn't exist, we can skip a lot of work. NycIndicator indicator = GetIndicator(indicatorId); retVal.Indicator = indicator.Name; retVal.Resolution = resolutionType.ToString(); // Now verify the query against the metadata. retVal.MinYear = (startYear < indicator.MinYear) ? indicator.MinYear : startYear; retVal.MaxYear = (endYear > indicator.MaxYear) ? indicator.MaxYear : endYear; bool validRequest = (retVal.MaxYear >= retVal.MinYear); if (!validRequest) { // Return a completely blank result object. return(retVal); } // We only want to load time metadata for times this indicator actually has data for. DaoJoinCriteria joinCrit = new DaoJoinCriteria(); joinCrit.RightCriteria = new DaoCriteria(); joinCrit.RightCriteria.Expressions.Add(new EqualExpression("IndicatorId", indicatorId)); joinCrit.RightCriteria.Expressions.Add(new EqualExpression("Resolution", resolutionType)); joinCrit.Expressions.Add(new EqualJoinExpression("UID", "TimeId")); // Load the time metadata. joinCrit.LeftCriteria = new DaoCriteria(); // These are not-ed so they are <= and >=; joinCrit.LeftCriteria.Expressions.Add(new GreaterExpression("Year", retVal.MaxYear, false)); joinCrit.LeftCriteria.Expressions.Add(new LesserExpression("Year", retVal.MinYear, false)); joinCrit.LeftCriteria.Expressions.Add(new EqualExpression("Type", timeUnitType)); // Sort by value descending, so the most recent year comes first. joinCrit.Orders.Add(new JoinSortOrder("Value", SortType.Asc, true)); List <JoinResult <NycTimeframe, NycResolutionForIndicator> > timeframes = _timeDao.Get(joinCrit, _resolutionDao); // We also need to know what all possible times are though, so we can render the fancy slider with appropriate gaps. joinCrit.LeftCriteria.Orders.Add(new SortOrder("Value", SortType.Asc)); IList <NycTimeframe> allTimeframes = _timeDao.Get(joinCrit.LeftCriteria); // Use them to assemble the metadata, since one year is one column. retVal.Attrs = new List <AbstractNamedSortable>(); retVal.Attrs.Add(new NycGeogColumnMetadata("Area")); IDictionary <object, int> colsByTimeId = new CheckedDictionary <object, int>(); foreach (JoinResult <NycTimeframe, NycResolutionForIndicator> timeframe in timeframes) { colsByTimeId[timeframe.Left.UID] = retVal.Attrs.Count; retVal.Attrs.Add(new NycYearColumnMetadata(timeframe.Left, indicator.ValueType)); } NycTableResults results = QueryNychanisData(indicatorId, resolutionType, scopeBorough, scopeSubborough, colsByTimeId); retVal.TotalResults = results.Values.Count; // Don't do any further processing if there are no results if (results.Values.Count == 0) { return(retVal); } // If the user specified a sort order, use that, otherwise sort by the first column (area). List <KeyValuePair <int, SortType> > colSorts = new List <KeyValuePair <int, SortType> >(); colSorts.Add(new KeyValuePair <int, SortType>(orderCol < 0 ? 0 : orderCol, orderDir ?? SortType.Asc)); results.Values.Sort(new MultipleColumnListComparer(colSorts)); // Truncate the results by the requested paging information retVal.Values = ResultsWithMetadata <AbstractNamedSortable> .GetPagedSubset(results.Values, numPerPage, page); // Now get context rows. Always get City, unless we're querying for City. if (resolutionType != NycResolutionType.City) { List <IList <object> > contextRows = QueryNychanisData(indicatorId, NycResolutionType.City, null, null, colsByTimeId).Values; // Now, if they provided a borough scope and didn't ask for a borough resolution, // include borough. if ((resolutionType != NycResolutionType.Borough) && (scopeBorough != null)) { contextRows.AddRange(QueryNychanisData(indicatorId, NycResolutionType.Borough, scopeBorough, null, colsByTimeId).Values); // Then if not subborough but a subborough is provided, include that. if ((resolutionType != NycResolutionType.SubBorough) && (scopeSubborough != null)) { contextRows.AddRange(QueryNychanisData(indicatorId, NycResolutionType.SubBorough, scopeBorough, scopeSubborough, colsByTimeId).Values); } } retVal.ContextRows = contextRows; } // Now generate the map info for showing the results as a cloropleth layer. Config cfg = Config.GetConfig("PDP.Data"); string sldHandlerUrl = cfg.GetParameter("Mapping", "SldHandlerURL"); retVal.MapInfo = new NycMapInfo(); retVal.MapInfo.Server = cfg.GetParameter("Mapping", "MapServerURL"); retVal.MapInfo.Layers = new List <NycLayerInfo>(); string layerName = Config.GetConfig("NYC.SLD").GetParameter("LayerNames", resolutionType.ToString(), null); // City doesn't have a map layer, so don't create any OL layers for it. if (layerName != null) { int possibleTimeIndex = 0; int actualTimeIndex = 0; while (possibleTimeIndex < allTimeframes.Count) { // We need to pad out the list of layers with blanks for timeframes that lack data. NycLayerInfo layer = new NycLayerInfo(); layer.Name = allTimeframes[possibleTimeIndex].Name; // Years that actually have data go up faster than all years, so check if the current // possible year is lower than the next actual year. if (allTimeframes[possibleTimeIndex].Value < timeframes[actualTimeIndex].Left.Value) { // Need to pad with a blank, so just let the blank layer object be added. // Increment the possible time index to the next timeframe. possibleTimeIndex++; } else { NycTimeframe time = timeframes[actualTimeIndex].Left; // I think this check is no longer necessary, since we're probably // always excluding unavailable data. But if it ain't broke... if (results.DataAvailableByTime[actualTimeIndex]) { layer.Config = new Dictionary <string, object>(); layer.Config["layers"] = layerName; layer.Config["styles"] = ""; layer.Config["format"] = "image/png"; layer.Config["tiled"] = true; layer.Config["srs"] = "EPSG:4326"; layer.Config["transparent"] = true; StringBuilder sb = new StringBuilder(sldHandlerUrl); sb.Append("?indicator=").Append(indicatorId); sb.Append("&resolution=").Append(resolutionType.ToString()); sb.Append("&time=").Append(time.UID); if (scopeBorough != null) { sb.Append("&borough=").Append(scopeBorough); if (scopeSubborough != null) { sb.Append("&subborough=").Append(scopeSubborough); } } layer.Config["SLD"] = sb.ToString(); } // Increment both indexes. possibleTimeIndex++; actualTimeIndex++; } retVal.MapInfo.Layers.Add(layer); } // If we are creating layers, we must create a legend to describe them retVal.LegendInfo = GenerateLegendList(indicator, resolutionType); } return(retVal); }