use of annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method splitRowsOnIslands.
/**
* Splits events of a row if they overlap an island. Islands are areas between
* the token which are included in the result.
*
* @param row
* @param graph
* @param text
* @param startTokenIndex token index of the first token in the match
* @param endTokenIndex token index of the last token in the match
*/
private static void splitRowsOnIslands(Row row, final SDocumentGraph graph, STextualDS text, long startTokenIndex, long endTokenIndex) {
BitSet tokenCoverage = new BitSet();
// get the sorted token
List<SToken> sortedTokenList = graph.getSortedTokenByText();
// add all token belonging to the right text to the bit set
ListIterator<SToken> itToken = sortedTokenList.listIterator();
while (itToken.hasNext()) {
SToken t = itToken.next();
if (text == null || text == CommonHelper.getTextualDSForNode(t, graph)) {
RelannisNodeFeature feat = (RelannisNodeFeature) t.getFeature(ANNIS_NS, FEAT_RELANNIS_NODE).getValue();
long tokenIndexRaw = feat.getTokenIndex();
tokenIndexRaw = clip(tokenIndexRaw, startTokenIndex, endTokenIndex);
int tokenIndex = (int) (tokenIndexRaw - startTokenIndex);
tokenCoverage.set(tokenIndex);
}
}
ListIterator<GridEvent> itEvents = row.getEvents().listIterator();
while (itEvents.hasNext()) {
GridEvent event = itEvents.next();
BitSet eventBitSet = new BitSet();
eventBitSet.set(event.getLeft(), event.getRight() + 1);
// restrict event bitset on the locations where token are present
eventBitSet.and(tokenCoverage);
// and we need to split it
if (eventBitSet.nextClearBit(event.getLeft()) <= event.getRight()) {
// remove the original event
row.removeEvent(itEvents);
// The event bitset now marks all the locations which the event should
// cover.
// Make a list of new events for each connected range in the bitset
int subElement = 0;
int offset = eventBitSet.nextSetBit(0);
while (offset >= 0) {
int end = eventBitSet.nextClearBit(offset) - 1;
if (offset < end) {
GridEvent newEvent = new GridEvent(event);
newEvent.setId(event.getId() + "_islandsplit_" + subElement++);
newEvent.setLeft(offset);
newEvent.setRight(end);
row.addEvent(itEvents, newEvent);
}
offset = eventBitSet.nextSetBit(end + 1);
}
}
// end if we need to split
}
}
use of annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method removeEmptySpace.
public static void removeEmptySpace(LinkedHashMap<String, ArrayList<Row>> rowsByAnnotation, Row tokenRow) {
List<Range<Integer>> gaps = new LinkedList<>();
BitSet totalOccupancyGrid = new BitSet();
for (Map.Entry<String, ArrayList<Row>> layer : rowsByAnnotation.entrySet()) {
for (Row r : layer.getValue()) {
totalOccupancyGrid.or(r.getOccupancyGridCopy());
}
}
// bug report.
if (tokenRow != null) {
totalOccupancyGrid.or(tokenRow.getOccupancyGridCopy());
}
// The Range class can give us the next bit that is not set. Use this
// to detect gaps. A gap starts from the next non-set bit and goes to
// the next set bit.
Range<Integer> gap = Range.closed(-1, totalOccupancyGrid.nextSetBit(0));
while (true) {
int gapStart = totalOccupancyGrid.nextClearBit(gap.upperEndpoint() + 1);
int gapEnd = totalOccupancyGrid.nextSetBit(gapStart);
if (gapEnd <= 0) {
break;
}
gap = Range.closed(gapStart, gapEnd - 1);
gaps.add(gap);
}
int gapID = 0;
int totalOffset = 0;
for (Range<Integer> gRaw : gaps) {
// adjust the space range itself
Range<Integer> g = Range.closed(gRaw.lowerEndpoint() - totalOffset, gRaw.upperEndpoint() - totalOffset);
int offset = g.upperEndpoint() - g.lowerEndpoint();
totalOffset += offset;
for (Entry<String, ArrayList<Row>> rowEntry : rowsByAnnotation.entrySet()) {
ArrayList<Row> rows = rowEntry.getValue();
for (Row r : rows) {
List<GridEvent> eventsCopy = new LinkedList<>(r.getEvents());
for (GridEvent e : eventsCopy) {
if (e.getLeft() >= g.upperEndpoint()) {
r.removeEvent(e);
e.setLeft(e.getLeft() - offset);
e.setRight(e.getRight() - offset);
r.addEvent(e);
}
}
// add a special space event
String spaceCaption = "";
if ("tok".equalsIgnoreCase(rowEntry.getKey())) {
spaceCaption = "(...)";
}
GridEvent spaceEvent = new GridEvent("gap-" + gapID, g.lowerEndpoint(), g.lowerEndpoint(), spaceCaption);
spaceEvent.setSpace(true);
r.addEvent(spaceEvent);
gapID++;
}
}
}
}
use of annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method addAnnotationsForNode.
private static void addAnnotationsForNode(SNode node, SDocumentGraph graph, long startTokenIndex, long endTokenIndex, PDFController pdfController, PDFPageHelper pageNumberHelper, AtomicInteger eventCounter, LinkedHashMap<String, ArrayList<Row>> rowsByAnnotation, boolean addMatch, Set<String> mediaLayer, boolean replaceValueWithMediaIcon) {
List<String> matchedAnnos = new ArrayList<>();
SFeature featMatchedAnnos = graph.getFeature(ANNIS_NS, FEAT_MATCHEDANNOS);
if (featMatchedAnnos != null) {
matchedAnnos = Splitter.on(',').trimResults().splitToList(featMatchedAnnos.getValue_STEXT());
}
// check if the span is a matched node
SFeature featMatched = node.getFeature(ANNIS_NS, FEAT_MATCHEDNODE);
Long matchRaw = featMatched == null ? null : featMatched.getValue_SNUMERIC();
String matchedQualifiedAnnoName = "";
if (matchRaw != null && matchRaw <= matchedAnnos.size()) {
matchedQualifiedAnnoName = matchedAnnos.get((int) ((long) matchRaw) - 1);
}
// calculate the left and right values of a span
// TODO: howto get these numbers with Salt?
RelannisNodeFeature feat = (RelannisNodeFeature) node.getFeature(ANNIS_NS, FEAT_RELANNIS_NODE).getValue();
long leftLong = feat.getLeftToken();
long rightLong = feat.getRightToken();
leftLong = clip(leftLong, startTokenIndex, endTokenIndex);
rightLong = clip(rightLong, startTokenIndex, endTokenIndex);
int left = (int) (leftLong - startTokenIndex);
int right = (int) (rightLong - startTokenIndex);
for (SAnnotation anno : node.getAnnotations()) {
ArrayList<Row> rows = rowsByAnnotation.get(anno.getQName());
if (rows == null) {
// try again with only the name
rows = rowsByAnnotation.get(anno.getName());
}
if (rows != null) {
// only do something if the annotation was defined before
// 1. give each annotation of each span an own row
Row r = new Row();
String id = "event_" + eventCounter.incrementAndGet();
GridEvent event = new GridEvent(id, left, right, anno.getValue_STEXT());
event.setTooltip(Helper.getQualifiedName(anno));
if (addMatch && matchRaw != null) {
long match = matchRaw;
if (matchedQualifiedAnnoName.isEmpty()) {
// always set the match when there is no matched annotation at all
event.setMatch(match);
} else // check if the annotation also matches
if (matchedQualifiedAnnoName.equals(anno.getQName())) {
event.setMatch(match);
}
}
if (node instanceof SSpan) {
// calculate overlapped SToken
List<? extends SRelation<? extends SNode, ? extends SNode>> outEdges = graph.getOutRelations(node.getId());
if (outEdges != null) {
for (SRelation<? extends SNode, ? extends SNode> e : outEdges) {
if (e instanceof SSpanningRelation) {
SSpanningRelation spanRel = (SSpanningRelation) e;
SToken tok = spanRel.getTarget();
event.getCoveredIDs().add(tok.getId());
// get the STextualDS of this token and add it to the event
String textID = getTextID(tok, graph);
if (textID != null) {
event.setTextID(textID);
}
}
}
}
// end if span has out edges
} else if (node instanceof SToken) {
event.getCoveredIDs().add(node.getId());
// get the STextualDS of this token and add it to the event
String textID = getTextID((SToken) node, graph);
if (textID != null) {
event.setTextID(textID);
}
}
// try to get time annotations
if (mediaLayer == null || mediaLayer.contains(anno.getQName())) {
double[] startEndTime = TimeHelper.getOverlappedTime(node);
if (startEndTime.length == 1) {
if (replaceValueWithMediaIcon) {
event.setValue(" ");
event.setTooltip("play excerpt " + event.getStartTime());
}
event.setStartTime(startEndTime[0]);
} else if (startEndTime.length == 2) {
event.setStartTime(startEndTime[0]);
event.setEndTime(startEndTime[1]);
if (replaceValueWithMediaIcon) {
event.setValue(" ");
event.setTooltip("play excerpt " + event.getStartTime() + "-" + event.getEndTime());
}
}
}
r.addEvent(event);
rows.add(r);
if (pdfController != null && pdfController.sizeOfRegisterdPDFViewer() > 0) {
String page = pageNumberHelper.getPageFromAnnotation(node);
if (page != null) {
event.setPage(page);
}
}
}
}
// end for each annotation of span
}
use of annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method splitRowsOnGaps.
/**
* Splits events of a row if they contain a gap. Gaps are found using the
* token index (provided as ANNIS specific {@link SFeature}. Inserted events
* have a special style to mark them as gaps.
*
* @param row
* @param graph
* @param startTokenIndex token index of the first token in the match
* @param endTokenIndex token index of the last token in the match
*/
private static void splitRowsOnGaps(Row row, final SDocumentGraph graph, long startTokenIndex, long endTokenIndex) {
ListIterator<GridEvent> itEvents = row.getEvents().listIterator();
while (itEvents.hasNext()) {
GridEvent event = itEvents.next();
int lastTokenIndex = -1;
// sort the coveredIDs
LinkedList<String> sortedCoveredToken = new LinkedList<>(event.getCoveredIDs());
Collections.sort(sortedCoveredToken, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
SNode node1 = graph.getNode(o1);
SNode node2 = graph.getNode(o2);
if (node1 == node2) {
return 0;
}
if (node1 == null) {
return -1;
}
if (node2 == null) {
return +1;
}
RelannisNodeFeature feat1 = (RelannisNodeFeature) node1.getFeature(ANNIS_NS, FEAT_RELANNIS_NODE).getValue();
RelannisNodeFeature feat2 = (RelannisNodeFeature) node2.getFeature(ANNIS_NS, FEAT_RELANNIS_NODE).getValue();
long tokenIndex1 = feat1.getTokenIndex();
long tokenIndex2 = feat2.getTokenIndex();
return ((Long) (tokenIndex1)).compareTo(tokenIndex2);
}
});
// first calculate all gaps
List<GridEvent> gaps = new LinkedList<>();
for (String id : sortedCoveredToken) {
SNode node = graph.getNode(id);
RelannisNodeFeature feat = (RelannisNodeFeature) node.getFeature(ANNIS_NS, FEAT_RELANNIS_NODE).getValue();
long tokenIndexRaw = feat.getTokenIndex();
tokenIndexRaw = clip(tokenIndexRaw, startTokenIndex, endTokenIndex);
int tokenIndex = (int) (tokenIndexRaw - startTokenIndex);
// sanity check
if (tokenIndex >= event.getLeft() && tokenIndex <= event.getRight()) {
int diff = tokenIndex - lastTokenIndex;
if (lastTokenIndex >= 0 && diff > 1) {
// we detected a gap
GridEvent gap = new GridEvent(event.getId() + "_gap_" + gaps.size(), lastTokenIndex + 1, tokenIndex - 1, "");
gap.setGap(true);
gaps.add(gap);
}
lastTokenIndex = tokenIndex;
} else {
// reset gap search when discovered there were token we use for
// hightlighting but do not actually cover
lastTokenIndex = -1;
}
}
// end for each covered token id
ListIterator<GridEvent> itGaps = gaps.listIterator();
// remember the old right value
int oldRight = event.getRight();
int gapNr = 0;
while (itGaps.hasNext()) {
GridEvent gap = itGaps.next();
if (gapNr == 0) {
// shorten original event
event.setRight(gap.getLeft() - 1);
}
// insert the real gap
itEvents.add(gap);
int rightBorder = oldRight;
if (itGaps.hasNext()) {
// don't use the old event right border since the gap should only go until
// the next event
GridEvent nextGap = itGaps.next();
itGaps.previous();
rightBorder = nextGap.getLeft() - 1;
}
// insert a new event node that covers the rest of the event
GridEvent after = new GridEvent(event);
after.setId(event.getId() + "_after_" + gapNr);
after.setLeft(gap.getRight() + 1);
after.setRight(rightBorder);
itEvents.add(after);
gapNr++;
}
}
}
use of annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class GridComponent method computeTokenRow.
private Row computeTokenRow(List<SNode> tokens, SDocumentGraph graph, LinkedHashMap<String, ArrayList<Row>> rowsByAnnotation, long startIndex, AtomicInteger tokenOffsetForText) {
/* we will only add tokens of one texts which is mentioned by any
included annotation. */
Set<String> validTextIDs = new HashSet<>();
if (enforcedText == null) {
Iterator<ArrayList<Row>> itAllRows = rowsByAnnotation.values().iterator();
while (itAllRows.hasNext()) {
ArrayList<Row> rowsForAnnotation = itAllRows.next();
for (Row r : rowsForAnnotation) {
validTextIDs.addAll(r.getTextIDs());
}
}
/**
* we want to show all token if no valid text was found and we have only one
* text and the first one if there are more than one text.
*/
List<STextualDS> allTexts = graph.getTextualDSs();
if (validTextIDs.isEmpty() && allTexts != null && (allTexts.size() == 1 || allTexts.size() == 2)) {
validTextIDs.add(allTexts.get(0).getId());
}
} else {
validTextIDs.add(enforcedText.getId());
}
Row tokenRow = new Row();
for (SNode t : tokens) {
// get the Salt ID of the STextualDS of this token
STextualDS tokenText = CommonHelper.getTextualDSForNode(t, graph);
// only add token if text ID matches the valid one
if (tokenText != null && validTextIDs.contains(tokenText.getId())) {
RelannisNodeFeature feat = (RelannisNodeFeature) t.getFeature(AnnisConstants.ANNIS_NS, AnnisConstants.FEAT_RELANNIS_NODE).getValue();
long idxLeft = feat.getLeftToken() - startIndex;
long idxRight = feat.getRightToken() - startIndex;
if (tokenOffsetForText.get() < 0) {
// set the token offset by assuming the first idx must be zero
tokenOffsetForText.set(Math.abs((int) idxLeft));
}
String text = extractTextForToken(t, segmentationName);
GridEvent event = new GridEvent(t.getId(), (int) idxLeft, (int) idxRight, text);
event.setTextID(tokenText.getId());
// check if the token is a matched node
Long match = isCoveredTokenMarked() ? markCoveredTokens(input.getMarkedAndCovered(), t) : tokenMatch(t);
event.setMatch(match);
tokenRow.addEvent(event);
}
}
return tokenRow;
}
Aggregations