Example #1
0
        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);
        }
Example #2
0
        /// <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);
        }