From 8a4a7337d0c4821610eb68b07ac08e6de6d1502a Mon Sep 17 00:00:00 2001
From: buczekp <buczekp>
Date: Thu, 10 Feb 2011 20:52:10 +0000
Subject: [PATCH] [LMS-2029] limit number of initially loaded features; load
 subsequent features on demand

SVN: 19889
---
 .../web/client/IScreeningClientService.java   |  9 ++
 .../client/IScreeningClientServiceAsync.java  | 11 ++-
 .../heatmaps/HeatmapPresenter.java            | 65 ++++++++++++--
 .../detailviewers/heatmaps/PlateLayouter.java | 44 +++++-----
 .../heatmaps/PlateLayouterModel.java          | 87 +++++++++++++++----
 .../web/server/ScreeningClientService.java    | 15 ++++
 .../server/FeatureCountLimitProvider.java     | 56 ++++++++++++
 .../server/IFeatureCountLimitProvider.java    | 29 +++++++
 .../IScreeningBusinessObjectFactory.java      |  5 +-
 .../ScreeningBusinessObjectFactory.java       |  8 ++
 .../screening/server/ScreeningServer.java     | 11 +++
 .../server/ScreeningServerLogger.java         | 11 +++
 .../server/logic/PlateContentLoader.java      | 78 +++++++++++++----
 .../screening/shared/IScreeningServer.java    | 16 +++-
 .../screening/shared/ResourceNames.java       |  2 +
 .../DatasetReferencePredicate.java            | 45 ++++++++++
 .../basic/dto/FeatureVectorDataset.java       | 13 +--
 .../java/screening-applicationContext.xml     |  4 +
 screening/source/java/service.properties      |  7 +-
 .../heatmaps/WellTooltipGeneratorTest.java    | 12 +--
 20 files changed, 450 insertions(+), 78 deletions(-)
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/FeatureCountLimitProvider.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IFeatureCountLimitProvider.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/authorization/DatasetReferencePredicate.java

diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientService.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientService.java
index f4c27f69fbc..8ccc4f191b0 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientService.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientService.java
@@ -25,11 +25,14 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteri
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TypedTableResultSet;
 import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleParentWithDerived;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageSampleContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LibraryRegistrationInfo;
@@ -70,6 +73,12 @@ public interface IScreeningClientService extends IClientService
      */
     public PlateContent getPlateContent(TechId sampleId) throws UserFailureException;
 
+    /**
+     * Fetches feature vector of specified dataset with one feature specified by name.
+     */
+    public FeatureVectorDataset getFeatureVectorDataset(DatasetReference dataset,
+            CodeAndLabel featureName);
+
     /**
      * Fetches information about a plate: metadata and images for wells. The specified dataset is
      * supposed to be in BDS-HCS format.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientServiceAsync.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientServiceAsync.java
index 18e0d5c31b9..4ccba731d5d 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientServiceAsync.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/IScreeningClientServiceAsync.java
@@ -16,6 +16,8 @@
 
 package ch.systemsx.cisd.openbis.plugin.screening.client.web.client;
 
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
 import ch.systemsx.cisd.openbis.generic.client.web.client.IClientServiceAsync;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListMaterialDisplayCriteria;
@@ -23,11 +25,14 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSet;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TypedTableResultSet;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleParentWithDerived;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageSampleContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LibraryRegistrationInfo;
@@ -39,8 +44,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellSearchCriteria;
 
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
 /**
  * Service interface for the <i>screening</i> <i>GWT</i> client.
  * 
@@ -56,6 +59,10 @@ public interface IScreeningClientServiceAsync extends IClientServiceAsync
     /** @see IScreeningClientService#getPlateContent(TechId) */
     public void getPlateContent(TechId sampleId, final AsyncCallback<PlateContent> callback);
 
+    /** @see IScreeningClientService#getFeatureVectorDataset(DatasetReference, CodeAndLabel) */
+    public void getFeatureVectorDataset(DatasetReference dataset, CodeAndLabel featureName,
+            AsyncCallback<FeatureVectorDataset> callback);
+
     /** @see IScreeningClientService#getPlateContentForDataset(TechId) */
     public void getPlateContentForDataset(TechId datasetId,
             AsyncCallback<PlateImages> createDisplayPlateCallback);
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/HeatmapPresenter.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/HeatmapPresenter.java
index f6597b8bf2d..4f154d29f75 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/HeatmapPresenter.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/HeatmapPresenter.java
@@ -8,17 +8,24 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import com.extjs.gxt.ui.client.widget.MessageBox;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Widget;
 
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.IRealNumberRenderer;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ScreeningViewContext;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.dto.WellData;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.heatmaps.dto.Color;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.heatmaps.dto.HeatmapScaleElement;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureValue;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata;
 
@@ -50,11 +57,15 @@ class HeatmapPresenter
 
     private final IRealNumberRenderer realNumberRenderer;
 
+    private final ScreeningViewContext viewContext;
+
     // ---
 
-    public HeatmapPresenter(PlateLayouterModel model, IRealNumberRenderer realNumberRenderer,
+    public HeatmapPresenter(ScreeningViewContext viewContext, PlateLayouterModel model,
+            IRealNumberRenderer realNumberRenderer,
             HeatmapPresenter.IHeatmapViewManipulator viewManipulations)
     {
+        this.viewContext = viewContext;
         this.model = model;
         this.viewManipulations = viewManipulations;
         this.realNumberRenderer = realNumberRenderer;
@@ -71,12 +82,53 @@ class HeatmapPresenter
     }
 
     /**
-     * Changes the presented heatmap to the one which shows the feature with given label.
+     * Changes the presented heatmap to the one which shows the feature with given name.
      */
-    public void setFeatureValueMode(String featureName)
+    public void setFeatureValueMode(CodeAndLabel featureName)
+    {
+        if (model.isFeatureAvailable(featureName.getLabel()))
+        {
+            doChangeFeatureValueMode(featureName);
+        } else
+        {
+            DatasetReference dataset = model.tryGetDatasetReference();
+            if (dataset != null)
+            {
+                viewContext.getService().getFeatureVectorDataset(dataset, featureName,
+                        createChangeHeatmapCallback(viewContext, featureName));
+            } else
+            {
+                MessageBox.alert("Error", "No data set selected", null);
+            }
+        }
+    }
+
+    private void doChangeFeatureValueMode(CodeAndLabel featureName)
     {
-        IHeatmapRenderer<WellData> renderer = createFeatureHeatmapRenderer(featureName);
-        refreshHeatmap(renderer, featureName);
+        IHeatmapRenderer<WellData> renderer = createFeatureHeatmapRenderer(featureName.getLabel());
+        refreshHeatmap(renderer, featureName.getLabel());
+    }
+
+    private AsyncCallback<FeatureVectorDataset> createChangeHeatmapCallback(
+            final ScreeningViewContext context, final CodeAndLabel featureName)
+    {
+        return new AbstractAsyncCallback<FeatureVectorDataset>(context)
+            {
+                @Override
+                protected void process(FeatureVectorDataset featureVector)
+                {
+                    model.updateFeatureVectorDataset(featureVector);
+
+                    if (model.isFeatureAvailable(featureName.getLabel()))
+                    {
+                        doChangeFeatureValueMode(featureName);
+                    } else
+                    {
+                        MessageBox.alert("Error",
+                                "No values found for feature " + featureName.getLabel(), null);
+                    }
+                }
+            };
     }
 
     private IHeatmapRenderer<WellData> createFeatureHeatmapRenderer(String featureLabel)
@@ -384,8 +436,7 @@ class HeatmapPresenter
 
         private int getNumberOfAllFeatures()
         {
-            List<String> featureLabels = model.tryGetFeatureLabels();
-            return featureLabels == null ? 0 : featureLabels.size();
+            return model.getAllFeatureNames().size();
         }
 
         private String generateOneFeatureDescription(WellData wellData, String featureLabel,
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouter.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouter.java
index adf5209da84..4f4e5e0b3ae 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouter.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouter.java
@@ -41,6 +41,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.SimpleModelComboBox;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.GWTUtils;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IMessageProvider;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningClientServiceAsync;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ScreeningViewContext;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.LayoutUtils;
@@ -99,7 +100,7 @@ public class PlateLayouter
 
     private final Widget view;
 
-    private final SimpleModelComboBox<String> heatmapKindChooser;
+    private final SimpleModelComboBox<CodeAndLabel> heatmapKindChooser;
 
     public PlateLayouter(ScreeningViewContext viewContext, PlateMetadata plateMetadata)
     {
@@ -108,7 +109,7 @@ public class PlateLayouter
         LayoutContainer legendContainer = new LayoutContainer();
         IRealNumberRenderer realNumberRenderer = createRealNumberRenderer(viewContext);
         this.presenter =
-                new HeatmapPresenter(model, realNumberRenderer, createViewManipulator(
+                new HeatmapPresenter(viewContext, model, realNumberRenderer, createViewManipulator(
                         renderedWells, legendContainer));
         this.heatmapKindChooser = createHeatmapKindComboBox(presenter, viewContext);
         this.view = renderView(renderedWells, heatmapKindChooser, legendContainer);
@@ -164,7 +165,7 @@ public class PlateLayouter
     public void changeDisplayedFeatureVectorDataset(FeatureVectorDataset dataset)
     {
         this.model.setFeatureVectorDataset(dataset);
-        updateHeatmapKindComboBox(heatmapKindChooser, model.tryGetFeatureLabels());
+        updateHeatmapKindComboBox(heatmapKindChooser, model.getAllFeatureNames());
     }
 
     /**
@@ -173,7 +174,7 @@ public class PlateLayouter
      * re-rendering.
      */
     private static Widget renderView(Component[][] renderedWells,
-            SimpleModelComboBox<String> heatmapKindChooser, LayoutContainer legendContainer)
+            SimpleModelComboBox<CodeAndLabel> heatmapKindChooser, LayoutContainer legendContainer)
     {
         LayoutContainer container = new LayoutContainer();
         container.setScrollMode(Scroll.AUTO);
@@ -370,20 +371,20 @@ public class PlateLayouter
 
     // ---------
 
-    private static SimpleModelComboBox<String> createHeatmapKindComboBox(
+    private static SimpleModelComboBox<CodeAndLabel> createHeatmapKindComboBox(
             final HeatmapPresenter presenter, IMessageProvider messageProvider)
     {
-        List<LabeledItem<String>> items = createHeatmapKindModel(null);
-        final SimpleModelComboBox<String> chooser =
-                new SimpleModelComboBox<String>(messageProvider, items,
+        List<LabeledItem<CodeAndLabel>> items = createHeatmapKindModel(null);
+        final SimpleModelComboBox<CodeAndLabel> chooser =
+                new SimpleModelComboBox<CodeAndLabel>(messageProvider, items,
                         HEATMAP_KIND_COMBOBOX_CHOOSER_WIDTH_PX);
-        chooser.addSelectionChangedListener(new SelectionChangedListener<SimpleComboValue<LabeledItem<String>>>()
+        chooser.addSelectionChangedListener(new SelectionChangedListener<SimpleComboValue<LabeledItem<CodeAndLabel>>>()
             {
                 @Override
                 public void selectionChanged(
-                        SelectionChangedEvent<SimpleComboValue<LabeledItem<String>>> se)
+                        SelectionChangedEvent<SimpleComboValue<LabeledItem<CodeAndLabel>>> se)
                 {
-                    String featureName = SimpleModelComboBox.getChosenItem(se);
+                    CodeAndLabel featureName = SimpleModelComboBox.getChosenItem(se);
                     if (featureName == null)
                     {
                         presenter.setWellMetadataMode();
@@ -396,25 +397,26 @@ public class PlateLayouter
         return chooser;
     }
 
-    private static void updateHeatmapKindComboBox(SimpleModelComboBox<String> chooser,
-            List<String> featureLabelsOrNull)
+    private static void updateHeatmapKindComboBox(SimpleModelComboBox<CodeAndLabel> chooser,
+            List<CodeAndLabel> featureNames)
     {
-        List<LabeledItem<String>> items = createHeatmapKindModel(featureLabelsOrNull);
+        List<LabeledItem<CodeAndLabel>> items = createHeatmapKindModel(featureNames);
         chooser.removeAll();
         chooser.add(items);
         GWTUtils.autoselect(chooser, false);
     }
 
-    private static List<LabeledItem<String>> createHeatmapKindModel(List<String> featureLabelsOrNull)
+    private static List<LabeledItem<CodeAndLabel>> createHeatmapKindModel(
+            List<CodeAndLabel> featureNamesOrNull)
     {
-        List<LabeledItem<String>> items = new ArrayList<LabeledItem<String>>();
-        items.add(new LabeledItem<String>(null, METADATA_HEATMAP_KIND_MSG));
-        if (featureLabelsOrNull != null)
+        List<LabeledItem<CodeAndLabel>> items = new ArrayList<LabeledItem<CodeAndLabel>>();
+        items.add(new LabeledItem<CodeAndLabel>(null, METADATA_HEATMAP_KIND_MSG));
+        if (featureNamesOrNull != null)
         {
-            for (String featureLabel : featureLabelsOrNull)
+            for (CodeAndLabel featureName : featureNamesOrNull)
             {
-                String label = FEATURE_HEATMAP_KIND_PREFIX_MSG + featureLabel;
-                items.add(new LabeledItem<String>(featureLabel, label));
+                String label = FEATURE_HEATMAP_KIND_PREFIX_MSG + featureName.getLabel();
+                items.add(new LabeledItem<CodeAndLabel>(featureName, label));
             }
         }
         return items;
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouterModel.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouterModel.java
index 564851df0dd..d7933d2dc7f 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouterModel.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/PlateLayouterModel.java
@@ -18,12 +18,15 @@ package ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
 
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.dto.WellData;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureValue;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorValues;
@@ -39,6 +42,8 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata;
  */
 class PlateLayouterModel
 {
+    private DatasetReference datasetReference; // currently shown dataset
+
     private final WellData[][] wellMatrix;
 
     private final List<WellData> wellList; // the same wells as in the matrix
@@ -47,8 +52,13 @@ class PlateLayouterModel
 
     private ImageDatasetEnrichedReference imageDatasetOrNull;
 
-    private List<String> featureLabelsOrNull; // names of all features
+    // names of all features
+    private List<CodeAndLabel> allFeatureNames = new ArrayList<CodeAndLabel>();
+
+    // labels of loaded features
+    private Set<String> availableFeatureLabels = new LinkedHashSet<String>();
 
+    // labels of loaded vocabulary features
     private Set<String> vocabularyFeatureLabels = new HashSet<String>();
 
     // ---
@@ -59,6 +69,11 @@ class PlateLayouterModel
         this.wellList = asList(wellMatrix);
     }
 
+    public DatasetReference tryGetDatasetReference()
+    {
+        return datasetReference;
+    }
+
     public WellData[][] getWellMatrix()
     {
         return wellMatrix;
@@ -79,9 +94,14 @@ class PlateLayouterModel
         this.imageDatasetOrNull = imageDataset;
     }
 
-    public List<String> tryGetFeatureLabels()
+    public List<CodeAndLabel> getAllFeatureNames()
     {
-        return featureLabelsOrNull;
+        return allFeatureNames;
+    }
+
+    public boolean isFeatureAvailable(String featureLabel)
+    {
+        return availableFeatureLabels.contains(featureLabel);
     }
 
     public boolean isVocabularyFeature(String featureLabel)
@@ -93,14 +113,12 @@ class PlateLayouterModel
 
     public void setFeatureVectorDataset(FeatureVectorDataset featureVectorDatasetOrNull)
     {
-        unsetFeatureVectors();
-        this.vocabularyFeatureLabels.clear(); // TODO PTR: don't clear on update
-        if (featureVectorDatasetOrNull == null)
+        cleanFeatureVectors();
+        this.datasetReference = null;
+        if (featureVectorDatasetOrNull != null)
         {
-            this.featureLabelsOrNull = null;
-        } else
-        {
-            this.featureLabelsOrNull = featureVectorDatasetOrNull.getFeatureLabels();
+            this.datasetReference = featureVectorDatasetOrNull.getDatasetReference();
+            this.allFeatureNames.addAll(featureVectorDatasetOrNull.getFeatureNames());
             List<? extends FeatureVectorValues> features =
                     featureVectorDatasetOrNull.getDatasetFeatures();
             if (features.isEmpty() == false)
@@ -118,6 +136,7 @@ class PlateLayouterModel
                             .entrySet())
                     {
                         String featureLabel = entry.getKey();
+                        availableFeatureLabels.add(featureLabel);
                         FeatureValue value = entry.getValue();
                         wellData.addFeatureValue(featureLabel, value);
                     }
@@ -126,6 +145,46 @@ class PlateLayouterModel
         }
     }
 
+    private void cleanFeatureVectors()
+    {
+        this.vocabularyFeatureLabels.clear();
+        this.availableFeatureLabels.clear();
+        this.allFeatureNames.clear();
+        for (WellData well : wellList)
+        {
+            well.resetFeatureValues();
+        }
+    }
+
+    // add new feature to those already loaded
+    public void updateFeatureVectorDataset(FeatureVectorDataset featureVectorDataset)
+    {
+        assert datasetReference.getCode().equals(
+                featureVectorDataset.getDatasetReference().getCode());
+
+        List<? extends FeatureVectorValues> features = featureVectorDataset.getDatasetFeatures();
+        if (features.isEmpty() == false)
+        {
+            // NOTE: for each feature vector in the dataset this set is the same
+            this.vocabularyFeatureLabels.addAll(extractVocabularyFeatureLabels(features.get(0)));
+        }
+        for (FeatureVectorValues featureVector : features)
+        {
+            WellLocation loc = featureVector.getWellLocation();
+            WellData wellData = tryGetWellData(loc);
+            if (wellData != null)
+            {
+                for (Entry<String, FeatureValue> entry : featureVector.getFeatureMap().entrySet())
+                {
+                    String featureLabel = entry.getKey();
+                    availableFeatureLabels.add(featureLabel);
+                    FeatureValue value = entry.getValue();
+                    wellData.addFeatureValue(featureLabel, value);
+                }
+            }
+        }
+    }
+
     private static Set<String> extractVocabularyFeatureLabels(FeatureVectorValues featureVector)
     {
         final Set<String> result = new HashSet<String>();
@@ -155,14 +214,6 @@ class PlateLayouterModel
         }
     }
 
-    private void unsetFeatureVectors()
-    {
-        for (WellData well : wellList)
-        {
-            well.resetFeatureValues(); // TODO PTR: needed? keep values of all datasets
-        }
-    }
-
     // ------------------------
 
     // Elements will NOT contain null even if well is empty.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/server/ScreeningClientService.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/server/ScreeningClientService.java
index fe82d66ea8c..c64bd44667a 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/server/ScreeningClientService.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/server/ScreeningClientService.java
@@ -40,6 +40,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.ITableModelP
 import ch.systemsx.cisd.openbis.generic.client.web.server.translator.UserFailureExceptionTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleParentWithDerived;
@@ -50,6 +51,8 @@ import ch.systemsx.cisd.openbis.plugin.screening.BuildAndEnvironmentInfo;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningClientService;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.IScreeningServer;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.ResourceNames;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageSampleContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LibraryRegistrationInfo;
@@ -141,6 +144,18 @@ public final class ScreeningClientService extends AbstractClientService implemen
         }
     }
 
+    public FeatureVectorDataset getFeatureVectorDataset(DatasetReference dataset,
+            CodeAndLabel featureName) throws UserFailureException
+    {
+        try
+        {
+            return server.getFeatureVectorDataset(getSessionToken(), dataset, featureName);
+        } catch (final ch.systemsx.cisd.common.exceptions.UserFailureException e)
+        {
+            throw UserFailureExceptionTranslator.translate(e);
+        }
+    }
+
     public PlateImages getPlateContentForDataset(TechId datasetId)
     {
         try
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/FeatureCountLimitProvider.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/FeatureCountLimitProvider.java
new file mode 100644
index 00000000000..6f9d2224cce
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/FeatureCountLimitProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.screening.server;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+
+import ch.systemsx.cisd.openbis.plugin.screening.shared.ResourceNames;
+
+/**
+ * The unique {@link IFeatureCountLimitProvider} implementation.
+ * 
+ * @author Piotr Buczek
+ */
+@Component(ResourceNames.FEATURE_COUNT_LIMIT_PROVIDER)
+public final class FeatureCountLimitProvider implements IFeatureCountLimitProvider
+{
+
+    private static int DEFAULT_FEATURE_COUNT_LIMIT = 5;
+
+    private int limit = DEFAULT_FEATURE_COUNT_LIMIT;
+
+    public FeatureCountLimitProvider()
+    {
+    }
+
+    public int getLimit()
+    {
+        return limit;
+    }
+
+    public void setLimitAsString(String limitAsString)
+    {
+        // This method is called by spring with value taken from properties file.
+        // Ignore the new value and keep default value when the property wasn't properly specified.
+        if (StringUtils.isNumeric(limitAsString))
+        {
+            limit = Integer.parseInt(limitAsString);
+        }
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IFeatureCountLimitProvider.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IFeatureCountLimitProvider.java
new file mode 100644
index 00000000000..bde4a8b02a5
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IFeatureCountLimitProvider.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.screening.server;
+
+/**
+ * Provides limit of features to be initially fetched for dataset feature vectors.
+ * 
+ * @author Piotr Buczek
+ */
+public interface IFeatureCountLimitProvider
+{
+
+    /** Returns max number of initially loaded features in total for all data sets. */
+    int getLimit();
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IScreeningBusinessObjectFactory.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IScreeningBusinessObjectFactory.java
index 38d205b8043..fffa130c737 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IScreeningBusinessObjectFactory.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/IScreeningBusinessObjectFactory.java
@@ -25,8 +25,8 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.datasetlister.IDatase
 import ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister.IMaterialLister;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.IImageDatasetLoader;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.IHCSFeatureVectorLoader;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.IImageDatasetLoader;
 
 /**
  * A <i>screening</i> plugin specific business object factory.
@@ -58,4 +58,7 @@ public interface IScreeningBusinessObjectFactory
     public IExternalDataBO createExternalDataBO(Session session);
 
     public IDatasetLister createDatasetLister(Session session);
+
+    public IFeatureCountLimitProvider getFeatureCountLimitProvider();
+
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningBusinessObjectFactory.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningBusinessObjectFactory.java
index 3eae7165207..bee199d05a9 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningBusinessObjectFactory.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningBusinessObjectFactory.java
@@ -57,6 +57,9 @@ public final class ScreeningBusinessObjectFactory extends AbstractPluginBusiness
     @Resource(name = ResourceNames.SCREENING_DAO_FACTORY)
     private IScreeningDAOFactory specificDAOFactory;
 
+    @Resource(name = ResourceNames.FEATURE_COUNT_LIMIT_PROVIDER)
+    private IFeatureCountLimitProvider featureCountLimitProvider;
+
     public ScreeningBusinessObjectFactory()
     {
     }
@@ -130,4 +133,9 @@ public final class ScreeningBusinessObjectFactory extends AbstractPluginBusiness
     {
         return getCommonBusinessObjectFactory().createDatasetLister(session);
     }
+
+    public IFeatureCountLimitProvider getFeatureCountLimitProvider()
+    {
+        return featureCountLimitProvider;
+    }
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
index 925b0325d80..ea0c08a324f 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
@@ -49,6 +49,7 @@ import ch.systemsx.cisd.openbis.generic.server.plugin.ISampleTypeSlaveServerPlug
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListMaterialCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
@@ -82,6 +83,8 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifi
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageSampleContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LogicalImageInfo;
@@ -169,6 +172,14 @@ public final class ScreeningServer extends AbstractServer<IScreeningServer> impl
         return PlateContentLoader.loadImagesAndMetadata(session, businessObjectFactory, plateId);
     }
 
+    public FeatureVectorDataset getFeatureVectorDataset(String sessionToken,
+            DatasetReference dataset, CodeAndLabel featureName)
+    {
+        Session session = getSession(sessionToken);
+        return PlateContentLoader.loadFeatureVectorDataset(session, businessObjectFactory, dataset,
+                featureName);
+    }
+
     public PlateImages getPlateContentForDataset(String sessionToken, TechId datasetId)
     {
         Session session = getSession(sessionToken);
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
index 3ba6a08231e..68e8f864811 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
@@ -25,6 +25,7 @@ import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerLogger;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
@@ -49,6 +50,8 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifi
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageSampleContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LogicalImageInfo;
@@ -91,6 +94,14 @@ final class ScreeningServerLogger extends AbstractServerLogger implements IScree
         logAccess(sessionToken, "getPlateContent", "PLATE(%s)", plateId.getId());
         return null;
     }
+    
+    public FeatureVectorDataset getFeatureVectorDataset(String sessionToken,
+            DatasetReference dataset, CodeAndLabel featureName)
+    {
+        logAccess(sessionToken, "getFeatureVectorDataset", "DATA_SET(%s) FEATURE(%s)", dataset.getCode(), featureName);
+        return null;
+    }
+
 
     public PlateImages getPlateContentForDataset(String sessionToken, TechId datasetId)
     {
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
index 3a4f4f6269f..a04bba32f56 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
@@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.plugin.screening.server.logic;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -27,13 +28,13 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister.IMaterialLister;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityPropertiesHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -70,9 +71,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.IImageDatasetLoa
  */
 public class PlateContentLoader
 {
-    // max number of initially loaded features
-    @SuppressWarnings("unused")
-    private static int MAX_FEATURES = 7;
 
     /**
      * Loads data about the plate for a specified sample id. Attaches information about images and
@@ -84,6 +82,17 @@ public class PlateContentLoader
         return new PlateContentLoader(session, businessObjectFactory).getPlateContent(plateId);
     }
 
+    /**
+     * Loads feature vector of specified dataset with one feature specified by name.
+     */
+    public static FeatureVectorDataset loadFeatureVectorDataset(Session session,
+            IScreeningBusinessObjectFactory businessObjectFactory, DatasetReference dataset,
+            CodeAndLabel featureName)
+    {
+        return new PlateContentLoader(session, businessObjectFactory).fetchFeatureVector(dataset,
+                featureName);
+    }
+
     /**
      * Loads data about the plate for a specified dataset, which is supposed to contain images.
      */
@@ -270,36 +279,73 @@ public class PlateContentLoader
         return fetchFeatureVectors(featureVectorDatasetReferences);
     }
 
+    private FeatureVectorDataset fetchFeatureVector(DatasetReference datasetReference,
+            CodeAndLabel featureName)
+    {
+        IHCSFeatureVectorLoader loader =
+                businessObjectFactory.createHCSFeatureVectorLoader(datasetReference
+                        .getDatastoreCode());
+        FeatureVectorDataset result = loadFeatureVector(datasetReference, featureName, loader);
+        return result;
+    }
+
     private List<FeatureVectorDataset> fetchFeatureVectors(
             List<DatasetReference> featureVectorDatasetReferences)
     {
         List<FeatureVectorDataset> featureVectorDatasets = new ArrayList<FeatureVectorDataset>();
-        for (DatasetReference datasetReference : featureVectorDatasetReferences)
+        if (featureVectorDatasetReferences.isEmpty() == false)
         {
-            IHCSFeatureVectorLoader loader =
-                    businessObjectFactory.createHCSFeatureVectorLoader(datasetReference
-                            .getDatastoreCode());
-            FeatureVectorDataset featureVectorDataset = loadFeatureVector(datasetReference, loader);
-            featureVectorDatasets.add(featureVectorDataset);
+            int maxFeatures = businessObjectFactory.getFeatureCountLimitProvider().getLimit();
+            int maxFeaturesPerDataset =
+                    Math.max(1, maxFeatures / featureVectorDatasetReferences.size());
+            for (DatasetReference datasetReference : featureVectorDatasetReferences)
+            {
+                IHCSFeatureVectorLoader loader =
+                        businessObjectFactory.createHCSFeatureVectorLoader(datasetReference
+                                .getDatastoreCode());
+                FeatureVectorDataset featureVectorDataset =
+                        loadFeatureVector(datasetReference, loader, maxFeaturesPerDataset);
+                featureVectorDatasets.add(featureVectorDataset);
+            }
         }
         return featureVectorDatasets;
     }
 
+    // loads feature vector with limited number of features
     private FeatureVectorDataset loadFeatureVector(DatasetReference datasetReference,
-            IHCSFeatureVectorLoader loader)
+            IHCSFeatureVectorLoader loader, int maxFeaturesPerDataset)
     {
+        List<CodeAndLabel> allFeatureNames =
+                loader.fetchDatasetFeatureNames(datasetReference.getCode());
+        List<CodeAndLabel> featuresToLoad = allFeatureNames;
+        if (featuresToLoad.size() > maxFeaturesPerDataset)
+        {
+            featuresToLoad = featuresToLoad.subList(0, maxFeaturesPerDataset);
+        }
+        WellFeatureCollection<FeatureVectorValues> featureValues =
+                loader.fetchDatasetFeatureValues(datasetReference.getCode(), featuresToLoad);
+        List<FeatureVectorValues> featureVectors = featureValues.getFeatures();
+
+        FeatureVectorDataset featureVectorDataset =
+                new FeatureVectorDataset(datasetReference, featureVectors, allFeatureNames);
+        return featureVectorDataset;
+    }
 
-        List<CodeAndLabel> featureNames =
+    // loads feature vector with only one feature specified by name
+    private FeatureVectorDataset loadFeatureVector(DatasetReference datasetReference,
+            CodeAndLabel featureName, IHCSFeatureVectorLoader loader)
+    {
+        List<CodeAndLabel> allFeatureNames =
                 loader.fetchDatasetFeatureNames(datasetReference.getCode());
-        // TODO PTR limit names
+
         WellFeatureCollection<FeatureVectorValues> featureValues =
-                loader.fetchDatasetFeatureValues(datasetReference.getCode(), featureNames);
+                loader.fetchDatasetFeatureValues(datasetReference.getCode(),
+                        Collections.singletonList(featureName));
 
         List<FeatureVectorValues> featureVectors = featureValues.getFeatures();
-        List<String> featureLabels = CodeAndLabel.asLabels(featureNames);
 
         FeatureVectorDataset featureVectorDataset =
-                new FeatureVectorDataset(datasetReference, featureVectors, featureLabels);
+                new FeatureVectorDataset(datasetReference, featureVectors, allFeatureNames);
         return featureVectorDataset;
     }
 
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java
index d0d16455309..befe4fbd8e9 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/IScreeningServer.java
@@ -30,6 +30,7 @@ import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.AbstractT
 import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.DataSetCodePredicate;
 import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.SampleTechIdPredicate;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
@@ -39,8 +40,11 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleParentWithDerived;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.authorization.DatasetReferencePredicate;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.authorization.WellContentValidator;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.authorization.WellSearchCriteriaPredicate;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageSampleContent;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LogicalImageInfo;
@@ -58,7 +62,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellSearchCrit
 public interface IScreeningServer extends IServer
 {
     /**
-     * loads data about the plate for a specified sample id. Attaches information about images and
+     * Loads data about the plate for a specified sample id. Attaches information about images and
      * image analysis only if one dataset with such a data exist.
      */
     @Transactional(readOnly = true)
@@ -66,6 +70,16 @@ public interface IScreeningServer extends IServer
     public PlateContent getPlateContent(String sessionToken,
             @AuthorizationGuard(guardClass = SampleTechIdPredicate.class) TechId plateId);
 
+    /**
+     * Loads feature vector of specified dataset with one feature specified by name.
+     */
+    @Transactional(readOnly = true)
+    @RolesAllowed(RoleWithHierarchy.SPACE_OBSERVER)
+    public FeatureVectorDataset getFeatureVectorDataset(
+            String sessionToken,
+            @AuthorizationGuard(guardClass = DatasetReferencePredicate.class) DatasetReference dataset,
+            CodeAndLabel featureName);
+
     /**
      * Returns plate content for a specified HCS_IMAGE dataset. Loads data about the plate for a
      * specified dataset, which is supposed to contain images in BDS-HCS format.
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/ResourceNames.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/ResourceNames.java
index cba01cf953c..8b545d76ec7 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/ResourceNames.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/ResourceNames.java
@@ -43,5 +43,7 @@ public final class ResourceNames
 
     public final static String SCREENING_SAMPLE_SERVER_PLUGIN = "screening-sample-server-plugin";
 
+    public final static String FEATURE_COUNT_LIMIT_PROVIDER = "feature-count-limit-provider";
+
     public static final String MAIL_CLIENT_PARAMETERS = "mail-client-parameters";
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/authorization/DatasetReferencePredicate.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/authorization/DatasetReferencePredicate.java
new file mode 100644
index 00000000000..7572b2e9e99
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/authorization/DatasetReferencePredicate.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.plugin.screening.shared.authorization;
+
+import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.DataSetCodePredicate;
+import ch.systemsx.cisd.openbis.generic.shared.authorization.predicate.DelegatedPredicate;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
+
+/**
+ * @author Piotr Buczek
+ */
+public final class DatasetReferencePredicate extends DelegatedPredicate<String, DatasetReference>
+{
+    public DatasetReferencePredicate()
+    {
+        super(new DataSetCodePredicate());
+    }
+
+    @Override
+    public final String getCandidateDescription()
+    {
+        return "dataset reference";
+    }
+
+    @Override
+    public String tryConvert(DatasetReference value)
+    {
+        return value.getCode();
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/FeatureVectorDataset.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/FeatureVectorDataset.java
index 8de439b7180..acbaa665f8a 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/FeatureVectorDataset.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/FeatureVectorDataset.java
@@ -19,6 +19,8 @@ package ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto;
 import java.util.List;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.ISerializable;
+import ch.systemsx.cisd.openbis.generic.shared.basic.annotation.DoNotEscape;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
@@ -26,13 +28,14 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
  * 
  * @author Tomasz Pylak
  */
+@DoNotEscape
 public class FeatureVectorDataset implements ISerializable
 {
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
     private DatasetReference datasetReference;
 
-    private List<String> featureLabels;
+    private List<CodeAndLabel> featureNames;
 
     private List<FeatureVectorValues> datasetFeatures;
 
@@ -43,11 +46,11 @@ public class FeatureVectorDataset implements ISerializable
     }
 
     public FeatureVectorDataset(DatasetReference datasetReference,
-            List<FeatureVectorValues> datasetFeatures, List<String> featureLabels)
+            List<FeatureVectorValues> datasetFeatures, List<CodeAndLabel> featureNames)
     {
         this.datasetReference = datasetReference;
         this.datasetFeatures = datasetFeatures;
-        this.featureLabels = featureLabels;
+        this.featureNames = featureNames;
     }
 
     public DatasetReference getDatasetReference()
@@ -55,9 +58,9 @@ public class FeatureVectorDataset implements ISerializable
         return datasetReference;
     }
 
-    public List<String> getFeatureLabels()
+    public List<CodeAndLabel> getFeatureNames()
     {
-        return featureLabels;
+        return featureNames;
     }
 
     public List<? extends FeatureVectorValues> getDatasetFeatures()
diff --git a/screening/source/java/screening-applicationContext.xml b/screening/source/java/screening-applicationContext.xml
index fb7b1f1a948..45bc6c0bf0b 100644
--- a/screening/source/java/screening-applicationContext.xml
+++ b/screening/source/java/screening-applicationContext.xml
@@ -18,6 +18,10 @@
         <constructor-arg ref="${data-source-provider}"/>
     </bean>
     
+    <bean id="feature-count-limit-provider" class="ch.systemsx.cisd.openbis.plugin.screening.server.FeatureCountLimitProvider">
+        <property name="limitAsString" value="${feature-count-limit}"/>        
+    </bean>
+    
     <!--
         //Mail Client Parameters
     -->
diff --git a/screening/source/java/service.properties b/screening/source/java/service.properties
index 68625e05929..803ff482785 100644
--- a/screening/source/java/service.properties
+++ b/screening/source/java/service.properties
@@ -55,7 +55,7 @@ cifex-recipient = id:cifexdatamanager
 hibernate.search.index-base = ./targets/indices-${database.kind}
 # One of NO_INDEX, SKIP_IF_MARKER_FOUND, INDEX_FROM_SCRATCH.
 # If not specified, default (SKIP_IF_MARKER_FOUND) is taken.
-hibernate.search.index-mode = NO_INDEX
+hibernate.search.index-mode = SKIP_IF_MARKER_FOUND
 #hibernate.search.index-mode = INDEX_FROM_SCRATCH
 # Defines the maximum number of elements indexed before flushing the transaction-bound queue.
 # Default is 1000.
@@ -66,4 +66,7 @@ hibernate.search.maxResults = 100000
 hibernate.search.worker.execution=async
 
 # Name of the file that stores Web Client configuration
-web-client-configuration-file = etc/web-client.properties
\ No newline at end of file
+web-client-configuration-file = etc/web-client.properties
+
+# (optional) max number of features to load initially for every Plate Layout view (default: 5) 
+feature-count-limit = 10
\ No newline at end of file
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/WellTooltipGeneratorTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/WellTooltipGeneratorTest.java
index 2a88079fcb2..05093da83b8 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/WellTooltipGeneratorTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/heatmaps/WellTooltipGeneratorTest.java
@@ -29,6 +29,7 @@ import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.common.test.AssertionUtil;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.IRealNumberRenderer;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
@@ -154,18 +155,18 @@ public class WellTooltipGeneratorTest extends AssertJUnit
     private static FeatureVectorDataset createLargeFeatureVectorDataset()
     {
         int size = 40;
-        List<String> featureLabels = new ArrayList<String>(size);
+        List<CodeAndLabel> featureNames = new ArrayList<CodeAndLabel>(size);
         Map<String, FeatureValue> featureValuesMap = new LinkedHashMap<String, FeatureValue>();
         for (int i = 0; i < size; i++)
         {
             final String label = "Feature" + i;
             final FeatureValue featureValue = FeatureValue.createFloat(i);
-            featureLabels.add("Feature" + i);
+            featureNames.add(new CodeAndLabel(label, label));
             featureValuesMap.put(label, featureValue);
         }
         List<FeatureVectorValues> features = new ArrayList<FeatureVectorValues>();
         features.add(new FeatureVectorValues(null, getLocation(WELL_A2), featureValuesMap));
-        return new FeatureVectorDataset(createDatasetReference(), features, featureLabels);
+        return new FeatureVectorDataset(createDatasetReference(), features, featureNames);
     }
 
     private static FeatureVectorDataset createFeatureVectorDataset()
@@ -181,8 +182,9 @@ public class WellTooltipGeneratorTest extends AssertJUnit
         features.add(new FeatureVectorValues(null, getLocation(WELL_B3), createFeatureVectorMap(
                 featureLabels, new FeatureValue[]
                     { FeatureValue.createFloat(-1), FeatureValue.createFloat(-2) })));
-        return new FeatureVectorDataset(createDatasetReference(), features,
-                Arrays.asList(featureLabels));
+        return new FeatureVectorDataset(createDatasetReference(), features, Arrays.asList(
+                new CodeAndLabel(featureLabels[0], featureLabels[0]), new CodeAndLabel(
+                        featureLabels[1], featureLabels[1])));
     }
 
     private static Map<String, FeatureValue> createFeatureVectorMap(String[] labels,
-- 
GitLab