root/FalconView/trunk/public/fvw_core/GeodataDataSources/LibkmlContainerBase.cpp @ 2272

Revision 2272, 19.3 KB (checked in by JO94, 6 months ago)

fixed repeated refreshing problem

Line 
1// Copyright (c) 1994-2010 Georgia Tech Research Corporation, Atlanta, GA
2// This file is part of FalconView(tm).
3
4// FalconView(tm) is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Lesser General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// FalconView(tm) is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public License
15// along with FalconView(tm).  If not, see <http://www.gnu.org/licenses/>.
16
17// FalconView(tm) is a trademark of Georgia Tech Research Corporation.
18
19#include "StdAfx.h"
20#include "LibkmlContainerBase.h"
21#include "LibkmlDataSource.h"
22#include "ComErrorObject.h"
23#include "kml/convenience/feature_list.h"
24#include "FvOGRPolygon.h"
25#include "UtilityMethods.h"
26
27// LibkmlContainerBase
28
29void LibkmlContainerBase::PopulateIndiciesAsNecessary()
30{
31   if (m_bool_indicies_populated) // may be reset within SetRegionationParameters
32      return;
33
34   bool bNeedToFireDataSourceChanged = false;
35
36   // KML containers contain KML "features" which can be place marks or other
37   // container types.  For this reason, this class implements both IFvDataSource
38   // and IFvDataSet.  This method builds (three) master vectors of all of the elements
39   // of the container that are data sources, that are data sets, and that are placemarks.
40
41   size_t size = m_container->get_feature_array_size();
42
43   for (UINT i = 0; i < size; i++)
44   {
45      kmldom::FeaturePtr feature = m_container->get_feature_array_at(i);
46
47      // If the regionation parameters have been set and the feature is not active,
48      // then we'll skip it.  Note that, if the regionation parameters have not been set
49      // then a container is considered active
50      if (m_degreesPerPixelX >= 0 && m_degreesPerPixelY >= 0 &&
51         !CUtilityMethods::KMLFeatureInRegion(feature, m_leftLon, m_bottomLat, m_rightLon, m_topLat, m_degreesPerPixelX, m_degreesPerPixelY))
52      {
53         continue;
54      }
55
56      // if we already have a COM object for this element, use it
57
58      UINT_TO_UNKNOWN_MAP::iterator it =  m_wrapped_objects.find(i);
59      IUnknownPtr unk;
60
61      if (it == m_wrapped_objects.end())
62      {
63         // this element has never been wrapped, so wrap it
64         unk = CLibkmlDataSource::WrapKMLElement(feature, m_kml_file, m_rootDS);
65         if (unk) // may return NULL for unsupported elements
66            unk->Release(); // get rid of extra reference
67         m_wrapped_objects[i] = unk;
68
69         // we've wrapped something, so need to fire a data source changed notification
70         bNeedToFireDataSourceChanged = true;
71      }
72      else
73      {
74         unk = it->second;
75      }
76
77      // save the object in the appropriate vector(s)
78
79      IFvDataSourcePtr spDataSource = unk;
80      if (spDataSource)
81         m_data_sources.push_back(spDataSource);
82
83      IFvDataSetPtr spDataSet = unk;
84      if (spDataSet)
85         m_data_sets.push_back(spDataSet);
86
87      IFeaturePtr spFeature = unk;
88      if (spFeature)
89      {
90         // save the feature and the index of the feature in the KML container
91         m_features.push_back(spFeature);
92         m_feature_indicies.push_back(i);
93      }
94   }
95
96   m_bool_all_elements_wrapped = m_wrapped_objects.size() == size; // optimization to prevent having to rebuild unnecessarily
97
98   m_bool_indicies_populated = TRUE; // set true here so that nested calls to this method get ignored
99
100   if (m_bool_indicies_never_populated) // prevents firing change notice the first time this ever loads
101      m_bool_indicies_never_populated = FALSE;
102   else if (bNeedToFireDataSourceChanged)
103      CLibkmlDataSource::FireDataSourceChanged(m_rootDS->Handle);
104}
105
106STDMETHODIMP LibkmlContainerBase::base_get_DataSetCount(LONG* count)
107{
108   TRY_BLOCK
109   {
110      PopulateIndiciesAsNecessary();
111      *count = (LONG)m_data_sets.size();
112   }
113   CATCH_BLOCK_RET
114
115   return S_OK;
116}
117
118STDMETHODIMP LibkmlContainerBase::base_raw_GetDataSet(LONG index, IFvDataSet** dataSet)
119{
120   TRY_BLOCK
121   {
122      PopulateIndiciesAsNecessary();
123      IFvDataSetPtr spDataSet = m_data_sets[index];
124      *dataSet = spDataSet.Detach();
125   }
126   CATCH_BLOCK_RET
127
128   return S_OK;
129}
130
131STDMETHODIMP LibkmlContainerBase::base_get_Name(BSTR* name)
132{
133   TRY_BLOCK
134   {
135      if (m_container->has_name())
136         *name = _bstr_t(m_container->get_name().c_str()).Detach();
137      else
138         *name = _bstr_t(m_tag_text.c_str()).Detach();
139   }
140   CATCH_BLOCK_RET
141
142   return S_OK;
143}
144
145STDMETHODIMP LibkmlContainerBase::base_get_Description(BSTR* description)
146{
147   TRY_BLOCK
148   {
149      if (m_container->has_description())
150         *description = _bstr_t(m_container->get_description().c_str()).Detach();
151      else
152      {
153         std::string s = "A ";
154         s.append(m_tag_text);
155         s.append(" element.");
156         *description = _bstr_t(s.c_str()).Detach();
157      }
158   }
159   CATCH_BLOCK_RET
160
161   return S_OK;
162}
163
164STDMETHODIMP LibkmlContainerBase::base_raw_RegisterForCallbacks(IFvDataSourceCallback* fvDataSourceCallback)
165{
166   TRY_BLOCK
167   {
168      IFvDataSourcePtr rootDS = m_rootDS;
169      return rootDS->RegisterForCallbacks(fvDataSourceCallback);;
170   }
171   CATCH_BLOCK_RET
172}
173
174STDMETHODIMP LibkmlContainerBase::base_raw_UnregisterForCallbacks(IFvDataSourceCallback* fvDataSourceCallback)
175{
176   TRY_BLOCK
177   {
178      IFvDataSourcePtr rootDS = m_rootDS;
179      return rootDS->UnregisterForCallbacks(fvDataSourceCallback);;
180   }
181   CATCH_BLOCK_RET
182}
183
184STDMETHODIMP LibkmlContainerBase::base_raw_GetDataSetByName(BSTR name_bstr, IFvDataSet** dataSet)
185{
186   return E_NOTIMPL;
187}
188
189void LibkmlContainerBase::AssociateWithLibkmlContainer(kmldom::ContainerPtr container, kmlengine::KmlFilePtr kml_file, ILibkmlDataSource* rootDS)
190{
191   rootDS->InternalRefAdding(); // see comments in InternalRefAdding implementation
192   m_rootDS = rootDS;
193   m_container = container;
194   m_kml_file = kml_file;
195}
196
197void LibkmlContainerBase::CleanupFeatureVector()
198{
199   delete m_features_in_filter; // okay to delete NULL
200   m_features_in_filter = NULL;
201}
202
203void LibkmlContainerBase::RebuildFeatureVectorAsNecessary()
204{
205   // This method builds m_features_in_filter, if it isn't built yet.  m_features_in_filter
206   // is an array of all of the features (placemarks) that match the current filter.
207
208   if (!m_features_in_filter)
209   {
210      PopulateIndiciesAsNecessary();
211      m_features_in_filter = new FEATURE_VECTOR();
212
213      // find a temporal filter within the filter
214      ITemporalFilterPtr temporal_filter = m_filter;
215      if (!temporal_filter)
216      {
217         ICompositeFilterPtr composite_filter = m_filter;
218         if (composite_filter)
219         {
220            for (LONG i = 0; i < composite_filter->NumFilters; i++)
221            {
222               IFilterPtr f = composite_filter->GetFilter(i);
223               temporal_filter = f;
224               if (temporal_filter)
225                  break;
226            }
227         }
228      }
229
230      // find a geometry filter within the filter
231      IGeometryFilterPtr geometry_filter = m_filter;
232      if (!geometry_filter)
233      {
234         ICompositeFilterPtr composite_filter = m_filter;
235         if (composite_filter)
236         {
237            for (LONG i = 0; i < composite_filter->NumFilters; i++)
238            {
239               IFilterPtr f = composite_filter->GetFilter(i);
240               geometry_filter = f;
241               if (geometry_filter)
242                  break;
243            }
244         }
245      }
246
247      // if we have a geometry filter, construct a spatial relation filter which will be used to find KML features
248      // whose extents intersect the filter
249
250      ISpatialRelationPtr spatialRelationFilter = NULL; // this will get set if we have a usuable filter
251      double minXFilter, minYFilter, maxXFilter, maxYFilter; // these will be the extents of the filter rectangle
252
253      if (geometry_filter)
254      {
255         // for performance, we currently only support filtering with a rectangle
256         IFilterPtr filter = geometry_filter;
257         CUtilityMethods::GetRectangularFilterBounds(filter, &minXFilter, &minYFilter, &maxXFilter, &maxYFilter);
258         CUtilityMethods::ShiftXWindowWithinM180To360(&minXFilter, &maxXFilter);
259         spatialRelationFilter = CFvOGRPolygon::constructPolygonFromEnvelope(minXFilter, maxYFilter, maxXFilter, minYFilter);
260         ULONG refCountAfter = spatialRelationFilter->Release(); // verified one reference count after this release
261      }
262
263      // find the kml features that match the filter criteria and add them to m_features_in_filter
264
265      for (FEATURE_VECTOR::size_type i = 0; i < m_features.size(); i++)
266      {
267         kmldom::FeaturePtr kml_feature = m_container->get_feature_array_at(m_feature_indicies[i]);
268
269         // apply any spatial filter
270         if (spatialRelationFilter)
271         {
272            // test whether the extents of the KML feature intersect the filter
273            if (!CUtilityMethods::KMLFeatureInRectangle(kml_feature, minXFilter, minYFilter, maxXFilter, maxYFilter))
274               continue;
275         }
276
277         // apply temporal filter, if there is one
278         if (temporal_filter && kml_feature->has_timeprimitive())
279         {
280            if (!CUtilityMethods::KMLTimePrimitiveInTemporalFilter(kml_feature->get_timeprimitive(), temporal_filter))
281               continue;
282         }
283
284         // if this feature is not in the region, skip it
285         if (m_degreesPerPixelX >= 0 && m_degreesPerPixelY >= 0
286            && !CUtilityMethods::KMLFeatureInRegion(
287            kml_feature, m_leftLon, m_bottomLat, m_rightLon, m_topLat, m_degreesPerPixelX, m_degreesPerPixelY))
288            continue;
289
290         // add this feature to the list
291         m_features_in_filter->push_back(m_features[i]);
292      }
293   }
294}
295
296STDMETHODIMP LibkmlContainerBase::base_get_FeatureCount(LONG* count)
297{
298   TRY_BLOCK
299   {
300      RebuildFeatureVectorAsNecessary();
301      *count = (LONG)m_features_in_filter->size();
302   }
303   CATCH_BLOCK_RET
304
305   return S_OK;
306}
307
308STDMETHODIMP LibkmlContainerBase::base_raw_GetFeature(LONG index, IFeature** feature)
309{
310   TRY_BLOCK
311   {
312      RebuildFeatureVectorAsNecessary();
313      IFeaturePtr f = m_features_in_filter->at(index);
314      *feature = f.Detach();
315   }
316   CATCH_BLOCK_RET
317
318   return S_OK;
319}
320
321STDMETHODIMP LibkmlContainerBase::base_raw_GetDataSource(LONG index, IFvDataSource** dataSource)
322{
323   TRY_BLOCK
324   {
325      PopulateIndiciesAsNecessary();
326      IFvDataSourcePtr spDataSource = m_data_sources[index];
327      *dataSource = spDataSource.Detach();
328   }
329   CATCH_BLOCK_RET
330
331   return S_OK;
332}
333
334STDMETHODIMP LibkmlContainerBase::base_get_DataSourceCount(LONG* count)
335{
336   TRY_BLOCK
337   {
338      PopulateIndiciesAsNecessary();
339      *count = (LONG)m_data_sources.size();
340   }
341   CATCH_BLOCK_RET
342
343   return S_OK;
344}
345
346STDMETHODIMP LibkmlContainerBase::base_raw_GetDataSourceByName(BSTR name, IFvDataSource** dataSource)
347{
348   return E_NOTIMPL;
349}
350
351STDMETHODIMP LibkmlContainerBase::base_get_Filter(IFilter** filter)
352{
353   ULONG refCountAfterAddRef;
354   if (m_filter)
355      refCountAfterAddRef = m_filter->AddRef();
356   *filter = m_filter;
357   return S_OK;
358}
359
360STDMETHODIMP LibkmlContainerBase::base_put_Filter(IFilter* filter)
361{
362   // !!!!!!!!!
363   // NOTE:  Currently only filters geometry on envelope.  TODO: smarter, fuller featured filtering
364   // !!!!!!!!!
365
366   TRY_BLOCK
367   {
368      // save the filter
369      m_filter = filter;
370
371      // the feature map must be rebuilt next time it is needed
372      CleanupFeatureVector();
373
374      // apply the filter to any contained data sets
375
376      PopulateIndiciesAsNecessary();
377
378      LONG count;
379      THROW_IF_NOT_OK(base_get_DataSetCount(&count));
380
381      for (int index = 0; index < count; index++)
382      {
383         IFvDataSet* dataSet;
384         THROW_IF_NOT_OK(base_raw_GetDataSet(index, &dataSet));
385         HRESULT hr = dataSet->put_Filter(filter);
386         ULONG refCountAfterRelease = dataSet->Release();
387         if (hr != E_NOTIMPL) // E_NOTIMPL is okay since not all data sets support filtering
388            THROW_IF_NOT_OK(hr);
389      }
390   }
391   CATCH_BLOCK_RET
392
393   return S_OK;
394}
395
396STDMETHODIMP LibkmlContainerBase::base_raw_GetImageByIdentifier(BSTR identifier, VARIANT* imageData)
397{
398   TRY_BLOCK
399   {
400      ISymbolFactoryPtr symbol_factory = m_rootDS;
401      return symbol_factory->raw_GetImageByIdentifier(identifier, imageData);
402   }
403   CATCH_BLOCK_RET
404}
405
406STDMETHODIMP LibkmlContainerBase::base_raw_Extent2D(double* minX, double* minY, double* maxX, double* maxY)
407{
408   TRY_BLOCK
409   {
410      kmlengine::Bbox bbox;
411      if (!kmlengine::GetFeatureBounds(m_container, &bbox))
412         return E_FAIL; // feature does not have bounds
413      *minX = bbox.get_west();
414      *minY = bbox.get_south();
415      *maxX = bbox.get_east();
416      *maxY = bbox.get_north();
417   }
418   CATCH_BLOCK_RET
419
420   return S_OK;
421}
422
423STDMETHODIMP LibkmlContainerBase::base_raw_StartEditing()
424{
425   // set the KML factory
426   m_factory = kmldom::KmlFactory::GetFactory();
427   return S_OK;
428}
429
430STDMETHODIMP LibkmlContainerBase::base_raw_CopyFeature(IFeature* feature)
431{
432   TRY_BLOCK
433   {
434      if (!m_factory)
435         THROW_ERROR_MSG2(E_FAIL, "must be in an editing session to copy a feature");
436      kmldom::PlacemarkPtr placemark = CLibkmlDataSource::WrapFeatureInPlacemark(feature, m_factory);
437      m_container->add_feature(placemark);
438   }
439   CATCH_BLOCK_RET
440
441   return S_OK;
442}
443
444STDMETHODIMP LibkmlContainerBase::base_raw_FinishEditing()
445{
446   m_factory = NULL;
447   return S_OK;
448}
449
450void LibkmlContainerBase::DoTemporalDataSetAnalysisAsNecessary()
451{
452   // call from within a TRY_BLOCK
453
454   if (m_bTemporalDataSetAnalysisComplete)
455      return;
456
457   m_bHasStart = FALSE;
458   m_bHasEnd = FALSE;
459   size_t size = m_container->get_feature_array_size();
460
461   for (UINT i = 0; i < size; i++)
462   {
463      kmldom::FeaturePtr feature = m_container->get_feature_array_at(i);
464      if (feature->has_timeprimitive())
465      {
466         kmldom::TimePrimitivePtr time_primitive = feature->get_timeprimitive();
467
468         if (CUtilityMethods::KMLTimePrimitiveHasStart(time_primitive))
469         {
470            DATE start = CUtilityMethods::KMLTimePrimitiveStart(time_primitive);
471            if (m_bHasStart)
472            {
473               if (start < m_dateStart)
474                  m_dateStart = start;
475            }
476            else
477            {
478               m_dateStart = start;
479               m_bHasStart = TRUE;
480            }
481         }
482
483         if (CUtilityMethods::KMLTimePrimitiveHasEnd(time_primitive))
484         {
485            DATE end = CUtilityMethods::KMLTimePrimitiveEnd(time_primitive);
486            if (m_bHasEnd)
487            {
488               if (end > m_dateEnd)
489                  m_dateEnd = end;
490            }
491            else
492            {
493               m_dateEnd = end;
494               m_bHasEnd = TRUE;
495            }
496         }
497      }
498   }
499
500   m_bTemporalDataSetAnalysisComplete = TRUE;
501}
502
503STDMETHODIMP LibkmlContainerBase::base_get_HasStart(VARIANT_BOOL* has_start)
504{
505   TRY_BLOCK
506   {
507      DoTemporalDataSetAnalysisAsNecessary();
508      *has_start = m_bHasStart;
509      return S_OK;
510   }
511   CATCH_BLOCK_RET
512}
513
514STDMETHODIMP LibkmlContainerBase::base_get_Start(DATE* start)
515{
516   TRY_BLOCK
517   {
518      DoTemporalDataSetAnalysisAsNecessary();
519      *start = m_dateStart;
520      return S_OK;
521   }
522   CATCH_BLOCK_RET
523}
524
525STDMETHODIMP LibkmlContainerBase::base_get_HasEnd(VARIANT_BOOL* has_end)
526{
527   TRY_BLOCK
528   {
529      DoTemporalDataSetAnalysisAsNecessary();
530      *has_end = m_bHasEnd;
531      return S_OK;
532   }
533   CATCH_BLOCK_RET
534}
535
536STDMETHODIMP LibkmlContainerBase::base_get_End(DATE* end)
537{
538   TRY_BLOCK
539   {
540      DoTemporalDataSetAnalysisAsNecessary();
541      *end = m_dateEnd;
542      return S_OK;
543   }
544   CATCH_BLOCK_RET
545}
546
547STDMETHODIMP LibkmlContainerBase::base_raw_SetRegionationParameters(
548   DOUBLE leftLon, DOUBLE bottomLat, DOUBLE rightLon, DOUBLE topLat,
549   DOUBLE degreesPerPixelX, DOUBLE degreesPerPixelY)
550{
551   TRY_BLOCK
552   {
553      // save the regionation parameters
554      m_leftLon = leftLon;
555      m_bottomLat = bottomLat;
556      m_rightLon = rightLon;
557      m_topLat = topLat;
558      m_degreesPerPixelX = degreesPerPixelX;
559      m_degreesPerPixelY = degreesPerPixelY;
560
561      // the feature map must be rebuilt next time it is needed
562      CleanupFeatureVector();
563
564      // force rebuild of indicies based on any new regionation stuff
565      if (!m_bool_all_elements_wrapped)
566      {
567         m_bool_indicies_populated = FALSE;
568         m_features.clear();
569         m_feature_indicies.clear();
570         m_data_sources.clear();
571         m_data_sets.clear();
572      }
573
574      // apply the parameters to any contained regionated objects
575
576      PopulateIndiciesAsNecessary();
577
578      LONG count;
579      THROW_IF_NOT_OK(base_get_DataSetCount(&count));
580
581      for (int index = 0; index < count; index++)
582      {
583         IFvDataSet* dataSet;
584         THROW_IF_NOT_OK(base_raw_GetDataSet(index, &dataSet));
585         IRegionatedKMLContainerPtr regionatedKMLContainer = dataSet;
586         if (regionatedKMLContainer)
587            regionatedKMLContainer->SetRegionationParameters(leftLon, bottomLat, rightLon, topLat, degreesPerPixelX, degreesPerPixelY);
588         dataSet->Release();
589      }
590
591      THROW_IF_NOT_OK(base_get_DataSourceCount(&count));
592
593      for (int index = 0; index < count; index++)
594      {
595         IFvDataSource* dataSource;
596         THROW_IF_NOT_OK(base_raw_GetDataSource(index, &dataSource));
597         IFvDataSetPtr dataSet = dataSource;
598         if (!dataSet) // skip data sets because we already did them above
599         {
600            IRegionatedKMLContainerPtr regionatedKMLContainer = dataSource;
601            if (regionatedKMLContainer)
602               regionatedKMLContainer->SetRegionationParameters(leftLon, bottomLat, rightLon, topLat, degreesPerPixelX, degreesPerPixelY);
603         }
604         dataSource->Release();
605      }
606
607      return S_OK;
608   }
609   CATCH_BLOCK_RET
610}
611
612STDMETHODIMP LibkmlContainerBase::base_raw_CheckConnectString(BSTR connectString, VARIANT_BOOL* valid)
613{
614   return E_NOTIMPL;
615}
616
617STDMETHODIMP LibkmlContainerBase::base_get_Visible(VARIANT_BOOL *visible)
618{
619   // Note that Google Earth apparently will set a container to visible if any of its children are
620   // visible.  The OGC standard doesn't seem to read this way, so we only look at the container itself.
621   *visible = m_container->has_visibility() ? (m_container->get_visibility() ? VARIANT_TRUE : VARIANT_FALSE) : VARIANT_TRUE;
622   return S_OK;
623}
624
625STDMETHODIMP LibkmlContainerBase::base_put_Visible(VARIANT_BOOL visible)
626{
627   m_container->set_visibility(visible ? true : false);
628   return S_OK;
629}
630
631STDMETHODIMP LibkmlContainerBase::base_get_Expanded(VARIANT_BOOL *expanded)
632{
633   *expanded = m_container->has_open() ? (m_container->get_open() ? VARIANT_TRUE : VARIANT_FALSE) : VARIANT_TRUE;
634   return S_OK;
635}
636
637STDMETHODIMP LibkmlContainerBase::base_put_Expanded(VARIANT_BOOL expanded)
638{
639   m_container->set_open(expanded ? true : false);
640   return S_OK;
641}
Note: See TracBrowser for help on using the browser.