001package de.kiezatlas; 002 003import de.deepamehta.core.Association; 004import de.deepamehta.plugins.accesscontrol.AccessControlService; 005import de.deepamehta.plugins.geomaps.GeomapsService; 006import de.deepamehta.plugins.facets.model.FacetValue; 007import de.deepamehta.plugins.facets.FacetsService; 008 009import de.deepamehta.core.AssociationDefinition; 010import de.deepamehta.core.RelatedTopic; 011import de.deepamehta.core.Topic; 012import de.deepamehta.core.model.AssociationModel; 013import de.deepamehta.core.model.ChildTopicsModel; 014import de.deepamehta.core.model.RelatedTopicModel; 015import de.deepamehta.core.model.TopicModel; 016import de.deepamehta.core.model.SimpleValue; 017import de.deepamehta.core.model.TopicRoleModel; 018import de.deepamehta.core.osgi.PluginActivator; 019import de.deepamehta.core.service.Cookies; 020import de.deepamehta.core.service.Inject; 021import de.deepamehta.core.service.ResultList; 022import de.deepamehta.core.service.Transactional; 023import de.deepamehta.core.service.event.PostUpdateTopicListener; 024import de.deepamehta.core.service.event.PreSendTopicListener; 025import de.deepamehta.core.util.DeepaMehtaUtils; 026import de.deepamehta.plugins.geomaps.model.GeoCoordinate; 027import de.deepamehta.plugins.time.TimeService; 028import de.deepamehta.plugins.workspaces.WorkspacesService; 029import java.io.InputStream; 030 031import javax.ws.rs.*; 032 033import java.util.ArrayList; 034import java.util.Iterator; 035import java.util.List; 036import java.util.logging.Logger; 037import javax.ws.rs.core.MediaType; 038import javax.ws.rs.core.Response; 039 040 041/** 042 * Kiezatlas 2 Plugin Service Implementation. 043 * @author jri & mukil 044 */ 045@Path("/site") 046@Consumes("application/json") 047@Produces("application/json") 048public class KiezatlasPlugin extends PluginActivator implements KiezatlasService, PostUpdateTopicListener, 049 PreSendTopicListener { 050 051 // ------------------------------------------------------------------------------------------------------- Constants 052 053 // The URIs of KA2 Geo Object topics have this prefix. 054 // The remaining part of the URI is the original KA1 topic id. 055 private static final String KA2_GEO_OBJECT_URI_PREFIX = "de.kiezatlas.topic."; 056 private static final String GEO_OBJECT_OWNER_PROPERTY = "de.kiezatlas.owner"; 057 private static final String GEO_OBJECT_KEYWORD_PROPERTY = "de.kiezatlas.key."; 058 059 // Website-Geomap association 060 private static final String WEBSITE_GEOMAP = "dm4.core.association"; 061 private static final String ROLE_TYPE_WEBSITE = "dm4.core.default"; // Note: used for both associations 062 private static final String ROLE_TYPE_GEOMAP = "dm4.core.default"; 063 // Website-Facet Types association 064 private static final String WEBSITE_FACET_TYPES = "dm4.core.association"; 065 private static final String ROLE_TYPE_FACET_TYPE = "dm4.core.default"; 066 067 // ---------------------------------------------------------------------------------------------- Instance Variables 068 069 @Inject private GeomapsService geomapsService; 070 @Inject private FacetsService facetsService; 071 @Inject private TimeService timeService; 072 @Inject private AccessControlService accessControlService; 073 @Inject private WorkspacesService workspaceService; 074 075 private Logger logger = Logger.getLogger(getClass().getName()); 076 077 // -------------------------------------------------------------------------------------------------- Public Methods 078 079 080 081 /** 082 * Fetches a simple page rendering objects for kiez-administrators. 083 * @return 084 */ 085 @GET 086 @Path("/administration") 087 @Produces(MediaType.TEXT_HTML) 088 public InputStream getKiezatlasAdministrationPage() { 089 String username = accessControlService.getUsername(); 090 if (username != null && !username.equals("admin")) throw new WebApplicationException(Response.Status.UNAUTHORIZED); 091 return getStaticResource("web/kiez-administration.html"); 092 } 093 094 095 096 // ********************** 097 // *** Plugin Service *** 098 // ********************** 099 100 @POST 101 @Path("/create/{siteName}/{siteUri}") 102 @Transactional 103 @Override 104 public Topic createWebsite(@PathParam("siteName") String siteName, @PathParam("siteUri") String siteUri) { 105 Topic websiteTopic = dms.getTopic("uri", new SimpleValue(siteUri)); 106 if (websiteTopic == null) { 107 logger.info("Creating Kiezatlas Website \"" + siteName + " with siteUri=\"" + siteUri + "\""); 108 websiteTopic = dms.createTopic(new TopicModel(siteUri, WEBSITE, new ChildTopicsModel() 109 .put("ka2.website.title", siteName))); 110 } else { 111 logger.info("Kiezatlas Website with siteUri=\"" + siteUri + "\" already exists"); 112 } 113 return websiteTopic; 114 } 115 116 @POST 117 @Path("/add/{geoObjectId}/{siteId}") 118 @Transactional 119 @Override 120 public Association addGeoObjectToWebsite(@PathParam("geoObjectId") long geoObjectId, @PathParam("siteId") long siteId) { 121 Topic geoObject = dms.getTopic(geoObjectId); 122 Association relation = null; 123 if (!hasSiteAssociation(geoObject, siteId)) { 124 logger.info("Adding Geo Object \"" + geoObject.getSimpleValue() + "\" to Site Topic: " + siteId); 125 relation = dms.createAssociation(new AssociationModel("dm4.core.association", 126 new TopicRoleModel(geoObjectId, "dm4.core.default"), new TopicRoleModel(siteId, "dm4.core.default"))); 127 } else { 128 logger.info("Skipping adding Topic to Site, Association already EXISTS"); 129 } 130 return relation; 131 } 132 133 @GET 134 @Path("/geomap/{geomap_id}") 135 @Override 136 public Topic getWebsite(@PathParam("geomap_id") long geomapId) { 137 try { 138 return dms.getTopic(geomapId).getRelatedTopic(WEBSITE_GEOMAP, ROLE_TYPE_WEBSITE, ROLE_TYPE_GEOMAP, 139 WEBSITE); 140 } catch (Exception e) { 141 throw new RuntimeException("Finding the geomap's website topic failed (geomapId=" + geomapId + ")", e); 142 } 143 } 144 145 @GET 146 @Path("/{website_id}/facets") 147 @Override 148 public ResultList<RelatedTopic> getFacetTypes(@PathParam("website_id") long websiteId) { 149 try { 150 return dms.getTopic(websiteId).getRelatedTopics(WEBSITE_FACET_TYPES, ROLE_TYPE_WEBSITE, 151 ROLE_TYPE_FACET_TYPE, "dm4.core.topic_type", 0); 152 } catch (Exception e) { 153 throw new RuntimeException("Finding the website's facet types failed (websiteId=" + websiteId + ")", e); 154 } 155 } 156 157 @GET 158 @Path("/criteria") 159 @Override 160 public List<Topic> getAllCriteria() { 161 List<Topic> criteria = dms.getTopics("uri", new SimpleValue("ka2.criteria.*")); 162 // remove facet types 163 Iterator<Topic> i = criteria.iterator(); 164 while (i.hasNext()) { 165 Topic crit = i.next(); 166 if (crit.getUri().endsWith(".facet")) { 167 i.remove(); 168 } 169 } 170 // 171 return criteria; 172 } 173 174 @GET 175 @Path("/geomap/{geomap_id}/objects") 176 @Override 177 public List<Topic> getGeoObjects(@PathParam("geomap_id") long geomapId) { 178 try { 179 return fetchGeoObjects(geomapId); 180 } catch (Exception e) { 181 throw new RuntimeException("Fetching the geomap's geo objects failed (geomapId=" + geomapId + ")", e); 182 } 183 } 184 185 @GET 186 @Path("/category/{id}/objects") 187 @Override 188 public List<RelatedTopic> getGeoObjectsByCategory(@PathParam("id") long categoryId) { 189 return dms.getTopic(categoryId).getRelatedTopics("dm4.core.aggregation", "dm4.core.child", "dm4.core.parent", 190 GEO_OBJECT, 0).getItems(); 191 } 192 193 /** Used by famportal editorial tool. */ 194 @GET 195 @Path("/geoobject") 196 @Override 197 public GeoObjects searchGeoObjectNames(@QueryParam("search") String searchTerm, @QueryParam("clock") long clock) { 198 GeoObjects result = new GeoObjects(clock); 199 for (Topic geoObjectName : dms.searchTopics("*" + searchTerm + "*", GEO_OBJECT_NAME)) { 200 result.add(getGeoObject(geoObjectName)); 201 } 202 return result; 203 } 204 205 @GET 206 @Path("/einrichtungen/missing-bezirk") 207 public List<SocialInstitutionObject> getInstitutionsMissingBezirk() { 208 logger.info("Fetching Social Institutions without a BEZIRK assignment"); 209 List<SocialInstitutionObject> results = new ArrayList<SocialInstitutionObject>(); 210 List<Topic> topics = dms.getTopics("type_uri", new SimpleValue("ka2.geo_object")); 211 for (Topic geoObject : topics) { 212 SocialInstitutionObject institution = new SocialInstitutionObject(); 213 institution.setName(geoObject.getSimpleValue().toString()); // ### load child topic "ka2.geo_object.name" 214 Topic bezirk = getFacettedBezirkChildTopic(geoObject); 215 if (bezirk == null) { 216 logger.warning("> Bezirks Relation is missing for " + geoObject.getSimpleValue()); 217 institution.setCreated(timeService.getCreationTime(geoObject.getId())); 218 institution.setLastModified(timeService.getModificationTime(geoObject.getId())); 219 institution.setUri(geoObject.getUri()); 220 results.add(institution); 221 } 222 } 223 return results; 224 } 225 226 @GET 227 @Path("/einrichtungen/missing-link/") 228 public List<SocialInstitutionObject> getInstitutionsMissingBacklink() { 229 logger.info("Fetching Social Institutions without a BEZIRK & BEZIRKSREGION assignment"); 230 List<SocialInstitutionObject> results = new ArrayList<SocialInstitutionObject>(); 231 List<Topic> topics = dms.getTopics("type_uri", new SimpleValue("ka2.geo_object")); 232 for (Topic geoObject : topics) { 233 SocialInstitutionObject institution = new SocialInstitutionObject(); 234 institution.setName(geoObject.getSimpleValue().toString()); // ### load child topic "ka2.geo_object.name" 235 Topic bezirk = getFacettedBezirkChildTopic(geoObject); 236 Topic bezirksregion = getFacettedBezirksregionChildTopic(geoObject); 237 if (bezirk == null && bezirksregion == null) { 238 logger.warning("> Bezirks and Bezirksregion Relation is missing for " + geoObject.getSimpleValue()); 239 institution.setCreated(timeService.getCreationTime(geoObject.getId())); 240 institution.setLastModified(timeService.getModificationTime(geoObject.getId())); 241 institution.setUri(geoObject.getUri()); 242 results.add(institution); 243 } 244 } 245 return results; 246 } 247 248 // Note: Fetch and build up administrative response objects 249 @GET 250 @Path("/einrichtungen/{id}") 251 public List<SocialInstitutionObject> getSiteInstitutions(@PathParam("id") long topicId) { 252 logger.info("Loading Social Institutions related to super Topic " + topicId); 253 List<SocialInstitutionObject> results = new ArrayList<SocialInstitutionObject>(); 254 Topic superTopic = dms.getTopic(topicId); 255 ResultList<RelatedTopic> geoObjects = superTopic.getRelatedTopics("dm4.core.aggregation", 256 "dm4.core.child", "dm4.core.parent", "ka2.geo_object", 0); 257 int missingMailboxes = 0; 258 for (RelatedTopic geoObject : geoObjects) { 259 SocialInstitutionObject institution = new SocialInstitutionObject(); 260 geoObject.loadChildTopics("dm4.contacts.address"); 261 RelatedTopic address = geoObject.getChildTopics().getTopic("dm4.contacts.address"); 262 Topic contactTopic = getFacettedContactChildTopic(geoObject); 263 String contactValue = "k.A."; 264 boolean missesMailbox = false; 265 if (contactTopic != null) { 266 contactTopic.loadChildTopics(); 267 Topic eMail = contactTopic.getChildTopics().getTopic("ka2.kontakt.email"); 268 if (eMail != null && !eMail.getSimpleValue().toString().isEmpty()) { 269 contactValue = eMail.getSimpleValue().toString(); 270 } else { 271 missesMailbox = true; 272 Topic phone = contactTopic.getChildTopics().getTopic("ka2.kontakt.telefon"); 273 if (phone != null && !phone.getSimpleValue().toString().isEmpty()) { 274 contactValue = phone.getSimpleValue().toString(); 275 } 276 } 277 } 278 GeoCoordinate coordinates = geomapsService.getGeoCoordinate(address); 279 if (coordinates != null) { 280 institution.setGeoCoordinate(coordinates); 281 } else { 282 institution.addClassification("no-coordinates"); 283 logger.info("> Geo Coordinates unavailable on \"" + geoObject.getSimpleValue().toString() + "\", Address: " + address.getSimpleValue()); 284 } 285 if (missesMailbox) { 286 // logger.info("> Identified missing Email Address at " + geoObject.getSimpleValue().toString()); 287 missingMailboxes++; 288 institution.addClassification("no-email"); 289 } 290 Topic bezirksregion = getFacettedBezirksregionChildTopic(geoObject); 291 if (bezirksregion == null) { 292 logger.warning("> No Bezirksregion set on \"" + geoObject.getSimpleValue().toString() + "\""); 293 institution.addClassification("no-bezirksregion"); 294 } 295 institution.setName(geoObject.getSimpleValue().toString()); // ### load child topic "ka2.geo_object.name" 296 institution.setContact(contactValue); 297 institution.setAddress(address.getSimpleValue().toString()); 298 institution.setCreated(timeService.getCreationTime(geoObject.getId())); 299 institution.setLastModified(timeService.getModificationTime(geoObject.getId())); 300 results.add(institution); 301 } 302 logger.info("> Identified " + missingMailboxes +" Geo Objects without an " 303 + "Email-Address in the group of \"" + superTopic.getSimpleValue().toString() 304 + "\" (" + geoObjects.getSize() + ")"); 305 return results; 306 } 307 308 @PUT 309 @Path("/geoobject/attribution/{topicId}/{owner}") 310 @Transactional 311 @Consumes(MediaType.TEXT_PLAIN) 312 public Response createGeoObjectAttribution(@PathParam("topicId") long id, 313 @PathParam("owner") String owner, String key) { 314 Topic geoObject = dms.getTopic(id); 315 if (geoObject != null && !owner.isEmpty() && !key.isEmpty()) { 316 String value = owner.trim(); 317 String keyValue = key.trim(); 318 try { 319 String existingValue = (String) geoObject.getProperty(GEO_OBJECT_OWNER_PROPERTY); 320 logger.warning("Values already set: Updating not allowed, owner=" + existingValue); 321 return Response.status(405).build(); 322 } catch (Exception e) { // ### org.neo4j.graphdb.NotFoundException 323 geoObject.setProperty(GEO_OBJECT_OWNER_PROPERTY, value, true); // ### addToIndex=true? 324 geoObject.setProperty(GEO_OBJECT_KEYWORD_PROPERTY, keyValue, false); 325 logger.info("### Equipped \"" + geoObject.getSimpleValue() + "\" with owner=\"" + owner + "\" and " + 326 "key=\"" + key + "\""); 327 return Response.status(200).build(); 328 } 329 } else if (owner.isEmpty()){ 330 logger.warning("Owner and/or key empty - Not allowed"); 331 return Response.status(405).build(); 332 } 333 return Response.status(404).build(); 334 } 335 336 @GET 337 @Path("/category/objects") 338 @Override 339 public GroupedGeoObjects searchCategories(@QueryParam("search") String searchTerm, 340 @QueryParam("clock") long clock) { 341 GroupedGeoObjects result = new GroupedGeoObjects(clock); 342 for (Topic criteria : getAllCriteria()) { 343 for (Topic category : dms.searchTopics("*" + searchTerm + "*", criteria.getUri())) { 344 result.add(criteria, category, getGeoObjectsByCategory(category.getId())); 345 } 346 } 347 return result; 348 } 349 350 @Override 351 public GeoCoordinate getGeoCoordinateByGeoObject(Topic geoObject) { 352 Topic addressTopic = geoObject.getChildTopics().getTopic(GEO_OBJECT_ADDRESS); 353 if (addressTopic != null) return geomapsService.getGeoCoordinate(addressTopic); 354 logger.warning("GeoCoordinate could not be determined becuase no Address is related to Geo Object: " 355 + geoObject.getSimpleValue()); 356 return null; 357 } 358 359 @Override 360 public String getGeoObjectAttribution(Topic geoObject) { 361 return (String) geoObject.getProperty(GEO_OBJECT_OWNER_PROPERTY) + ":" 362 + (String) geoObject.getProperty(GEO_OBJECT_KEYWORD_PROPERTY); 363 } 364 365 /** This facet depends/just exists after installation of the dm4-kiezatlas-etl plugin. */ 366 @Override 367 public Topic getFacettedBezirksregionChildTopic(Topic facettedTopic) { 368 // Note: Untested 369 return facetsService.getFacet(facettedTopic, "ka2.bezirksregion.facet"); 370 } 371 372 /** This facet depends/just exists after installation of the dm4-kiezatlas-etl plugin. */ 373 @Override 374 public Topic getImageFileFacetByGeoObject(Topic geoObject) { 375 return facetsService.getFacet(geoObject, "ka2.bild.facet"); 376 } 377 378 @Override 379 public ResultList<RelatedTopic> getParentRelatedAggregatedGeoObjects(Topic bezirksFacet) { 380 return bezirksFacet.getRelatedTopics("dm4.core.aggregation", "dm4.core.child", 381 "dm4.core.parent", "ka2.geo_object", 0); 382 } 383 384 @Override 385 public void updateImageFileFacet(Topic geoObject, String imageFilePath) { 386 facetsService.updateFacet(geoObject, "ka2.bild.facet", new FacetValue("ka2.bild.pfad").put(imageFilePath)); 387 } 388 389 // ******************************** 390 // *** Listener Implementations *** 391 // ******************************** 392 393 394 395 @Override 396 public void preSendTopic(Topic topic) { 397 if (!topic.getTypeUri().equals(GEO_OBJECT)) { 398 return; 399 } 400 // 401 ResultList<RelatedTopic> facetTypes = getFacetTypes(); 402 if (facetTypes == null) { 403 return; 404 } 405 // 406 enrichWithFacets(topic, facetTypes); 407 } 408 409 @Override 410 public void postUpdateTopic(Topic topic, TopicModel newModel, TopicModel oldModel) { 411 if (!topic.getTypeUri().equals(GEO_OBJECT)) { 412 return; 413 } 414 // 415 ResultList<RelatedTopic> facetTypes = getFacetTypes(); 416 if (facetTypes == null) { 417 return; 418 } 419 // 420 updateFacets(topic, facetTypes, newModel); 421 } 422 423 424 425 // ------------------------------------------------------------------------------------------------- Private Methods 426 427 private Topic getFacettedContactChildTopic(Topic facettedTopic) { 428 return facettedTopic.getRelatedTopic("dm4.core.composition", "dm4.core.parent", 429 "dm4.core.child", "ka2.kontakt"); 430 } 431 432 private Topic getFacettedBezirkChildTopic(Topic facettedTopic) { 433 return facettedTopic.getRelatedTopic("dm4.core.aggregation", "dm4.core.parent", 434 "dm4.core.child", "ka2.bezirk"); 435 } 436 437 private boolean hasSiteAssociation(Topic geoObject, long siteId) { 438 ResultList<RelatedTopic> sites = geoObject.getRelatedTopics("dm4.core.association", "dm4.core.default", 439 "dm4.core.default", WEBSITE, 0); 440 for (RelatedTopic site : sites) { 441 if (site.getId() == siteId) return true; 442 } 443 return false; 444 } 445 446 // === Enrich with facets === 447 448 private void enrichWithFacets(Topic geoObject, ResultList<RelatedTopic> facetTypes) { 449 for (Topic facetType : facetTypes) { 450 String facetTypeUri = facetType.getUri(); 451 if (!isMultiFacet(facetTypeUri)) { 452 enrichWithSingleFacet(geoObject, facetTypeUri); 453 } else { 454 enrichWithMultiFacet(geoObject, facetTypeUri); 455 } 456 } 457 } 458 459 // --- 460 461 private void enrichWithSingleFacet(Topic geoObject, String facetTypeUri) { 462 Topic facetValue = facetsService.getFacet(geoObject, facetTypeUri); 463 // Note: facetValue is null in 2 cases: 464 // 1) The geo object has just been created (no update yet) 465 // 2) The geo object has been created outside a geomap and then being revealed in a geomap. 466 if (facetValue == null) { 467 logger.info("### Enriching geo object " + geoObject.getId() + " with its \"" + facetTypeUri + 468 "\" facet value ABORTED -- no such facet in DB"); 469 return; 470 } 471 // 472 logger.info("### Enriching geo object " + geoObject.getId() + " with its \"" + facetTypeUri + 473 "\" facet value (" + facetValue + ")"); 474 geoObject.getChildTopics().getModel().put(facetValue.getTypeUri(), facetValue.getModel()); 475 } 476 477 private void enrichWithMultiFacet(Topic geoObject, String facetTypeUri) { 478 ResultList<RelatedTopic> facetValues = facetsService.getFacets(geoObject, facetTypeUri); 479 logger.info("### Enriching geo object " + geoObject.getId() + " with its \"" + facetTypeUri + 480 "\" facet values (" + facetValues + ")"); 481 String childTypeUri = getChildTypeUri(facetTypeUri); 482 // Note: we set the facet values at once (using put()) instead of iterating (and using add()) as after an geo 483 // object update request the facet values are already set. Using add() would result in having the values twice. 484 geoObject.getChildTopics().getModel().put(childTypeUri, DeepaMehtaUtils.toTopicModels(facetValues)); 485 } 486 487 488 489 // === Update facets === 490 491 private void updateFacets(Topic geoObject, ResultList<RelatedTopic> facetTypes, TopicModel newModel) { 492 for (Topic facetType : facetTypes) { 493 String facetTypeUri = facetType.getUri(); 494 String childTypeUri = getChildTypeUri(facetTypeUri); 495 if (!isMultiFacet(facetTypeUri)) { 496 TopicModel facetValue = newModel.getChildTopicsModel().getTopic(childTypeUri); 497 logger.info("### Storing facet of type \"" + facetTypeUri + "\" for geo object " + geoObject.getId() + 498 " (facetValue=" + facetValue + ")"); 499 FacetValue value = new FacetValue(childTypeUri).put(facetValue); 500 facetsService.updateFacet(geoObject, facetTypeUri, value); 501 } else { 502 List<RelatedTopicModel> facetValues = newModel.getChildTopicsModel().getTopics(childTypeUri); 503 logger.info("### Storing facets of type \"" + facetTypeUri + "\" for geo object " + geoObject.getId() + 504 " (facetValues=" + facetValues + ")"); 505 FacetValue value = new FacetValue(childTypeUri).put(facetValues); 506 facetsService.updateFacet(geoObject, facetTypeUri, value); 507 } 508 } 509 } 510 511 512 513 // === Helper === 514 515 /** 516 * Returns the facet types for the current topicmap, or null if the facet types can't be determined. 517 * There can be several reasons for the latter: 518 * a) there is no "current topicmap". This can be the case with 3rd-party clients. 519 * b) the current topicmap is not a geomap. 520 * c) the geomap is not part of a Kiezatlas Website. 521 * 522 * @return The facet types (as a result set, may be empty), or <code>null</code>. 523 */ 524 private ResultList<RelatedTopic> getFacetTypes() { 525 Cookies cookies = Cookies.get(); 526 if (!cookies.has("dm4_topicmap_id")) { 527 logger.fine("### Finding geo object facet types ABORTED -- topicmap is unknown (no \"dm4_topicmap_id\" " + 528 "cookie was sent)"); 529 return null; 530 } 531 // 532 long topicmapId = cookies.getLong("dm4_topicmap_id"); 533 if (!isGeomap(topicmapId)) { 534 logger.fine("### Finding geo object facet types for topicmap " + topicmapId + " ABORTED -- not a geomap"); 535 return null; 536 } 537 // 538 Topic website = getWebsite(topicmapId); 539 if (website == null) { 540 logger.info("### Finding geo object facet types for geomap " + topicmapId + " ABORTED -- not part of a " + 541 "Kiezatlas website"); 542 return null; 543 } 544 // 545 logger.info("### Finding geo object facet types for geomap " + topicmapId); 546 return getFacetTypes(website.getId()); 547 } 548 549 private List<Topic> fetchGeoObjects(long geomapId) { 550 List<Topic> geoObjects = new ArrayList<Topic>(); 551 for (TopicModel geoCoord : geomapsService.getGeomap(geomapId)) { 552 Topic geoObject = geomapsService.getDomainTopic(geoCoord.getId()); 553 geoObjects.add(geoObject); 554 // ### TODO: optimization. Include only name and address in returned geo objects. 555 // ### For the moment the entire objects are returned, including composite values and facets. 556 } 557 return geoObjects; 558 } 559 560 // --- 561 562 private Topic getGeoObject(Topic geoObjectName) { 563 return geoObjectName.getRelatedTopic("dm4.core.composition", "dm4.core.child", "dm4.core.parent", 564 GEO_OBJECT); // ### TODO: Core API should provide type-driven navigation 565 } 566 567 private boolean isGeomap(long topicmapId) { 568 Topic topicmap = dms.getTopic(topicmapId); 569 String rendererUri = topicmap.getChildTopics().getString("dm4.topicmaps.topicmap_renderer_uri"); 570 return rendererUri.equals("dm4.geomaps.geomap_renderer"); 571 } 572 573 // --- 574 575 // ### FIXME: there is a copy in FacetsPlugin.java 576 private boolean isMultiFacet(String facetTypeUri) { 577 return getAssocDef(facetTypeUri).getChildCardinalityUri().equals("dm4.core.many"); 578 } 579 580 // ### FIXME: there is a copy in FacetsPlugin.java 581 private String getChildTypeUri(String facetTypeUri) { 582 return getAssocDef(facetTypeUri).getChildTypeUri(); 583 } 584 585 // ### FIXME: there is a copy in FacetsPlugin.java 586 private AssociationDefinition getAssocDef(String facetTypeUri) { 587 // Note: a facet type has exactly *one* association definition 588 return dms.getTopicType(facetTypeUri).getAssocDefs().iterator().next(); 589 } 590}