Merge branch 'master_16.1.2p3_for_merges' into master_16.2.1-p1
Conflicts: cave/com.raytheon.uf.viz.spellchecker/src/com/raytheon/uf/viz/spellchecker/dialogs/SpellCheckDlg.java cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gis/Area.java Change-Id: I944b2f48026df080e494ab9104bfd63e30c0c9ef Former-commit-id: f8ebc36d5e1edc7f625bfafc3f8e9d8a7d13303d
This commit is contained in:
commit
0960d09481
5 changed files with 542 additions and 125 deletions
|
@ -88,7 +88,9 @@ import com.raytheon.uf.viz.spellchecker.jobs.SpellCheckJob;
|
|||
* 08/31/2015 #4781 dgilling Improve handling of proper nouns in all
|
||||
* caps mode, move override dictionary to
|
||||
* SITE level.
|
||||
*
|
||||
* 05/25/2016 DR16930 MPorricelli Added suggestionsBlackList to spellCheckJob
|
||||
* for flagging of words that are in
|
||||
* inappropriateWords.txt blacklist
|
||||
* </pre>
|
||||
*
|
||||
* @author lvenable
|
||||
|
@ -257,6 +259,7 @@ public class SpellCheckDlg extends Dialog implements ISpellingProblemCollector {
|
|||
spellCheckJob.setCollector(this);
|
||||
|
||||
suggestionsBlacklist = getSuggestionsBlacklist();
|
||||
spellCheckJob.setBlacklist(suggestionsBlacklist);
|
||||
}
|
||||
|
||||
private Collection<String> getSuggestionsBlacklist() {
|
||||
|
|
|
@ -23,7 +23,15 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
|
@ -56,6 +64,8 @@ import com.raytheon.uf.viz.spellchecker.Activator;
|
|||
* 01Mar2010 4765 MW Fegan Moved from GFE plug-in.
|
||||
* 18Oct2010 11237 rferrel Created readLine in order to compute
|
||||
* offsets for start of line correctly.
|
||||
* 25May2016 DR16930 MPorricelli Added flagging of words that are in
|
||||
* inappropriateWords.txt blacklist
|
||||
* </pre>
|
||||
*
|
||||
* @author wldougher
|
||||
|
@ -92,6 +102,8 @@ public class SpellCheckJob extends Job implements ISpellingProblemCollector {
|
|||
|
||||
private String line;
|
||||
|
||||
private Collection<String> blackList;
|
||||
|
||||
private String vtecRegex = new String(
|
||||
"/[OTEX]\\.([A-Z]{3})\\.([A-Z]{4})\\.([A-Z]{2})\\."
|
||||
+ "([WAYSOFN])\\.([0-9]{4})\\.([0-9]{6})T([0-9]{4})Z-"
|
||||
|
@ -220,8 +232,55 @@ public class SpellCheckJob extends Job implements ISpellingProblemCollector {
|
|||
synchronized (this) {
|
||||
service.check(document, regions, context, this, monitor);
|
||||
}
|
||||
// Look in this line for words that are in
|
||||
// inappropriateWords.txt
|
||||
// If one is found and it has not already been flagged by the
|
||||
// Eclipse spell check above, add it to problems
|
||||
Pattern pattern = Pattern.compile("\\w+");
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
while (matcher.find()) {
|
||||
String currentWord = matcher.group();
|
||||
if (blackList != null
|
||||
&& blackList.contains(currentWord.toUpperCase())) {
|
||||
int length = currentWord.length();
|
||||
int offset = matcher.start();
|
||||
SpellingProblem blacklistProblem = new AnotherSpellingProblem(
|
||||
offset, length, currentWord);
|
||||
if (problems.isEmpty())
|
||||
problems.add(blacklistProblem);
|
||||
else {
|
||||
Boolean problemAlreadyFlagged = false;
|
||||
Iterator<SpellingProblem> probIter = problems
|
||||
.iterator();
|
||||
while (probIter.hasNext()) {
|
||||
if (probIter.next().getOffset() == blacklistProblem
|
||||
.getOffset()) {
|
||||
problemAlreadyFlagged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!problemAlreadyFlagged)
|
||||
problems.add(blacklistProblem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort the problems by position (offset) in this line of text
|
||||
List<SpellingProblem> problemsList;
|
||||
problemsList = new ArrayList<SpellingProblem>();
|
||||
if (!problems.isEmpty()) {
|
||||
problemsList.addAll(problems);
|
||||
Collections.sort(problemsList, new Comparator<SpellingProblem>() {
|
||||
@Override
|
||||
public int compare(SpellingProblem sp1, SpellingProblem sp2) {
|
||||
return Integer.valueOf(sp1.getOffset()).compareTo(
|
||||
Integer.valueOf(sp2.getOffset()));
|
||||
}
|
||||
});
|
||||
problems.clear();
|
||||
problems.addAll(problemsList);
|
||||
}
|
||||
|
||||
if (problems.size() > 0) {
|
||||
SpellingProblem lineProblem = problems.pollFirst();
|
||||
|
@ -327,6 +386,12 @@ public class SpellCheckJob extends Job implements ISpellingProblemCollector {
|
|||
this.collector = collector;
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlacklist(Collection<String> suggestionsBlacklist) {
|
||||
synchronized (this) {
|
||||
this.blackList = suggestionsBlacklist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -388,3 +453,49 @@ class RevisedProblem extends SpellingProblem {
|
|||
return lineProblem.getProposals();
|
||||
}
|
||||
}
|
||||
|
||||
class AnotherSpellingProblem extends SpellingProblem {
|
||||
int offset;
|
||||
int length;
|
||||
String message;
|
||||
|
||||
AnotherSpellingProblem(int offSet, int wordLength, String word) {
|
||||
super();
|
||||
this.offset = offSet;
|
||||
this.length = wordLength;
|
||||
this.message = "The word '" + word + "' is not correctly spelled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
public void setOffset(int offSet) {
|
||||
this.offset = offSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
public void setLength(int wordLength) {
|
||||
this.length = wordLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public void setMessage(String word) {
|
||||
this.message = "The word '" + word + "' is not correctly spelled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICompletionProposal[] getProposals() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,22 @@
|
|||
package com.raytheon.viz.warngen.gis;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
||||
|
@ -35,6 +45,7 @@ import com.raytheon.uf.common.dataplugin.warning.config.GeospatialConfiguration;
|
|||
import com.raytheon.uf.common.dataplugin.warning.config.WarngenConfiguration;
|
||||
import com.raytheon.uf.common.dataplugin.warning.gis.GeospatialData;
|
||||
import com.raytheon.uf.common.dataplugin.warning.portions.GisUtil;
|
||||
import com.raytheon.uf.common.dataplugin.warning.portions.GisUtil.Direction;
|
||||
import com.raytheon.uf.common.dataplugin.warning.portions.PortionsUtil;
|
||||
import com.raytheon.uf.common.dataplugin.warning.util.CountyUserData;
|
||||
import com.raytheon.uf.common.dataplugin.warning.util.GeometryUtil;
|
||||
|
@ -50,11 +61,17 @@ import com.raytheon.uf.common.status.PerformanceStatus;
|
|||
import com.raytheon.uf.common.status.UFStatus;
|
||||
import com.raytheon.uf.common.status.UFStatus.Priority;
|
||||
import com.raytheon.uf.viz.core.exception.VizException;
|
||||
//import com.raytheon.viz.warngen.gis.GisUtil.Direction;
|
||||
import com.raytheon.uf.common.dataplugin.warning.portions.GisUtil.Direction;
|
||||
import com.raytheon.uf.viz.core.localization.LocalizationManager;
|
||||
import com.raytheon.viz.warngen.gui.WarngenLayer;
|
||||
import com.raytheon.viz.warngen.util.Abbreviation;
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
import com.vividsolutions.jts.geom.Envelope;
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.GeometryCollection;
|
||||
import com.vividsolutions.jts.geom.GeometryFactory;
|
||||
import com.vividsolutions.jts.geom.Polygon;
|
||||
import com.vividsolutions.jts.geom.Polygonal;
|
||||
import com.vividsolutions.jts.geom.TopologyException;
|
||||
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
|
||||
|
||||
/**
|
||||
|
@ -91,6 +108,7 @@ import com.vividsolutions.jts.geom.prep.PreparedGeometry;
|
|||
* Mar 9, 2014 ASM #17190 D. Friedman Use fipsField and areaField for unique area ID.
|
||||
* May 7, 2015 ASM #17438 D. Friedman Clean up debug and performance logging.
|
||||
* Dec 15, 2015 ASM #17933 mgamazaychikov Update calculation of partOfParentRegion.
|
||||
* May 12, 2016 ASM #18789 D. Friedman Improve findInsectingAreas performance.
|
||||
* </pre>
|
||||
*
|
||||
* @author chammack
|
||||
|
@ -113,6 +131,16 @@ public class Area {
|
|||
.asList(new String[] { "PA", "MI", "PD", "UP", "BB", "ER", "EU",
|
||||
"SR", "NR", "WU", "DS" });
|
||||
|
||||
private static final int DEFAULT_SUBDIVISION_TRESHOLD = 100;
|
||||
|
||||
private static final int SIMPLE_FEATURE_GEOM_COUNT_THRESHOLD = 2;
|
||||
|
||||
private static final int MAX_SUBDIVISION_DEPTH = 24;
|
||||
|
||||
private static final String SUBDIVISION_CONFIG_FILE = "subdiv.txt";
|
||||
|
||||
private static ExecutorService intersectionExecutor;
|
||||
|
||||
private PortionsUtil portionsUtil;
|
||||
|
||||
public Area(PortionsUtil portionsUtil) {
|
||||
|
@ -327,7 +355,6 @@ public class Area {
|
|||
Geometry warnPolygon, Geometry warnArea, String localizedSite,
|
||||
WarngenLayer warngenLayer) throws VizException {
|
||||
Map<String, Object> areasMap = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
Geometry precisionReducedArea = PolygonUtil
|
||||
.reducePrecision(warnArea);
|
||||
|
@ -340,25 +367,74 @@ public class Area {
|
|||
|
||||
String hatchedAreaSource = config.getHatchedAreaSource()
|
||||
.getAreaSource();
|
||||
|
||||
boolean subdivide = true;
|
||||
try {
|
||||
String propertiesText = WarnFileUtil.convertFileContentsToString(
|
||||
SUBDIVISION_CONFIG_FILE, LocalizationManager.getInstance()
|
||||
.getCurrentSite(), warngenLayer.getLocalizedSite());
|
||||
if (propertiesText != null) {
|
||||
Properties props = new Properties();
|
||||
props.load(new StringReader(propertiesText));
|
||||
subdivide = Boolean.parseBoolean(props.getProperty("enabled", "true"));
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// ignore
|
||||
} catch (IOException e) {
|
||||
statusHandler.handle(Priority.WARN, "Could not load subdivision configuration file", e);
|
||||
}
|
||||
if (!subdivide) {
|
||||
statusHandler.debug("findIntersectingAreas: subdivision is disabled");
|
||||
}
|
||||
|
||||
long t0 = System.currentTimeMillis();
|
||||
for (AreaSourceConfiguration asc : config.getAreaSources()) {
|
||||
boolean ignoreUserData = asc.getAreaSource().equals(
|
||||
hatchedAreaSource) == false;
|
||||
if (asc.getType() == AreaType.INTERSECT) {
|
||||
List<Geometry> geoms = new ArrayList<Geometry>();
|
||||
boolean filtered = false;
|
||||
for (GeospatialData f : warngenLayer.getGeodataFeatures(
|
||||
asc.getAreaSource(), localizedSite)) {
|
||||
|
||||
boolean ignoreUserData = asc.getAreaSource().equals(
|
||||
hatchedAreaSource) == false;
|
||||
Geometry intersect = GeometryUtil.intersection(warnArea,
|
||||
f.prepGeom, ignoreUserData);
|
||||
|
||||
filtered = false;
|
||||
if (!intersect.isEmpty()) {
|
||||
filtered = warngenLayer.filterArea(f, intersect, asc);
|
||||
if (subdivide && ignoreUserData) {
|
||||
synchronized (Area.class) {
|
||||
if (intersectionExecutor == null) {
|
||||
int nThreads = Math.max(2,
|
||||
Runtime.getRuntime().availableProcessors() / 2);
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
||||
nThreads, nThreads, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>());
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
intersectionExecutor = executor;
|
||||
}
|
||||
}
|
||||
|
||||
if (intersect.isEmpty() == false && filtered == true) {
|
||||
geoms.add(intersect);
|
||||
Geometry waPoly = toPolygonal(warnArea);
|
||||
GeospatialData[] features = warngenLayer.getGeodataFeatures(
|
||||
asc.getAreaSource(), localizedSite);
|
||||
List<Callable<Geometry>> callables = new ArrayList<>(features.length);
|
||||
for (GeospatialData f : features) {
|
||||
callables.add(new FeatureIntersection(waPoly, f));
|
||||
}
|
||||
try {
|
||||
List<Future<Geometry>> futures = intersectionExecutor.invokeAll(callables);
|
||||
int fi = 0;
|
||||
for (Future<Geometry> future: futures) {
|
||||
Geometry intersect = future.get();
|
||||
if (intersect != null && !intersect.isEmpty()
|
||||
&& warngenLayer.filterArea(features[fi], intersect, asc)) {
|
||||
geoms.add(intersect);
|
||||
}
|
||||
fi++;
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new VizException("Error finding intersecting areas", e);
|
||||
}
|
||||
} else {
|
||||
for (GeospatialData f : warngenLayer.getGeodataFeatures(
|
||||
asc.getAreaSource(), localizedSite)) {
|
||||
Geometry intersect = GeometryUtil.intersection(
|
||||
warnArea, f.prepGeom, ignoreUserData);
|
||||
if (!intersect.isEmpty()
|
||||
&& warngenLayer.filterArea(f, intersect, asc)) {
|
||||
geoms.add(intersect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,9 +445,198 @@ public class Area {
|
|||
areasMap.put(asc.getVariable(), affectedAreas);
|
||||
}
|
||||
}
|
||||
perfLog.logDuration("findIntersectingAreas", System.currentTimeMillis() - t0);
|
||||
|
||||
return areasMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert input to Polygon or Multipolygon. This will discard any
|
||||
* non-polygon elements.
|
||||
*/
|
||||
private static Geometry toPolygonal(Geometry input) {
|
||||
Geometry result;
|
||||
if (input instanceof Polygonal) {
|
||||
result = input;
|
||||
} else {
|
||||
List<Polygon> pa = new ArrayList<>(input.getNumGeometries() + 63);
|
||||
toPolygonalInner(input, pa);
|
||||
result = input.getFactory().createMultiPolygon(pa.toArray(new Polygon[pa.size()]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void toPolygonalInner(Geometry input, List<Polygon> pa) {
|
||||
int n = input.getNumGeometries();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Geometry g = input.getGeometryN(i);
|
||||
if (g instanceof Polygon) {
|
||||
pa.add((Polygon) g);
|
||||
} else if (g instanceof GeometryCollection) {
|
||||
toPolygonalInner(g, pa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class FeatureIntersection implements Callable<Geometry> {
|
||||
private Geometry waPoly;
|
||||
private GeospatialData f;
|
||||
|
||||
public FeatureIntersection(Geometry waPoly, GeospatialData f) {
|
||||
this.waPoly = waPoly;
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Geometry call() throws Exception {
|
||||
Geometry intersect = null;
|
||||
if (f.prepGeom.intersects(waPoly)) {
|
||||
try {
|
||||
Geometry fgPoly = toPolygonal(f.geometry);
|
||||
List<Geometry> out = new ArrayList<Geometry>(64);
|
||||
subdivIntersect(waPoly, fgPoly, true, out);
|
||||
intersect = waPoly.getFactory().createGeometryCollection(
|
||||
out.toArray(new Geometry[out.size()]));
|
||||
// subdivIntersect loses user data to set it again.
|
||||
intersect.setUserData(f.geometry.getUserData());
|
||||
} catch (TopologyException e) {
|
||||
intersect = GeometryUtil.intersection(waPoly,
|
||||
f.prepGeom, true);
|
||||
}
|
||||
}
|
||||
return intersect;
|
||||
}
|
||||
}
|
||||
|
||||
private static void subdivIntersect(Geometry warnArea, Geometry featureGeom,
|
||||
boolean orient, List<Geometry> out) {
|
||||
Envelope env = warnArea.getEnvelopeInternal().intersection(
|
||||
featureGeom.getEnvelopeInternal());
|
||||
if (env.isNull()) {
|
||||
return;
|
||||
}
|
||||
Coordinate[] c = new Coordinate[5];
|
||||
c[0] = new Coordinate(env.getMinX(), env.getMinY());
|
||||
c[1] = new Coordinate(env.getMaxX(), env.getMinY());
|
||||
c[2] = new Coordinate(env.getMaxX(), env.getMaxY());
|
||||
c[3] = new Coordinate(env.getMinX(), env.getMaxY());
|
||||
c[4] = c[0];
|
||||
subdivIntersectInner(c, warnArea, featureGeom, orient, 1, out);
|
||||
}
|
||||
|
||||
private static void subdivIntersectInner(Coordinate[] env, Geometry warnArea,
|
||||
Geometry featureGeom, boolean orientation, int depth,
|
||||
List<Geometry> out) {
|
||||
if (warnArea.getNumGeometries() * featureGeom.getNumGeometries() <= DEFAULT_SUBDIVISION_TRESHOLD
|
||||
|| depth >= MAX_SUBDIVISION_DEPTH) {
|
||||
out.add(batchIntersect(warnArea, featureGeom));
|
||||
} else if (featureGeom.getNumGeometries() <= SIMPLE_FEATURE_GEOM_COUNT_THRESHOLD) {
|
||||
try {
|
||||
Polygon clipPoly = warnArea.getFactory().createPolygon(env);
|
||||
Geometry clippedWarnArea = clip(clipPoly, warnArea);
|
||||
/*
|
||||
* Not clipping feature geometry because it is already known to
|
||||
* have a small geometry count.
|
||||
*/
|
||||
out.add(batchIntersect(clippedWarnArea, featureGeom));
|
||||
} catch (TopologyException e) {
|
||||
// Additional fallback without clipping.
|
||||
statusHandler.handle(Priority.DEBUG,
|
||||
"Clipped intersection failed. Will attempt fallback.", e);
|
||||
out.add(GeometryUtil.intersection(warnArea, featureGeom, true));
|
||||
}
|
||||
} else {
|
||||
GeometryFactory gf = warnArea.getFactory();
|
||||
Coordinate[] c = new Coordinate[5];
|
||||
List<Geometry> subOut = new ArrayList<>();
|
||||
for (int side = 0; side < 2; ++side) {
|
||||
if (side == 0) {
|
||||
if (orientation) {
|
||||
// horizontal split
|
||||
c[0] = env[0];
|
||||
c[1] = new Coordinate((env[0].x + env[1].x) / 2, env[0].y);
|
||||
c[2] = new Coordinate(c[1].x, env[2].y);
|
||||
c[3] = env[3];
|
||||
c[4] = c[0];
|
||||
} else {
|
||||
// vertical split
|
||||
c[0] = env[0];
|
||||
c[1] = env[1];
|
||||
c[2] = new Coordinate(c[1].x, (env[0].y + env[3].y) / 2);
|
||||
c[3] = new Coordinate(c[0].x, c[2].y);
|
||||
c[4] = c[0];
|
||||
}
|
||||
} else {
|
||||
if (orientation) {
|
||||
c[0] = c[1];
|
||||
c[3] = c[2];
|
||||
c[1] = env[1];
|
||||
c[2] = env[2];
|
||||
c[4] = c[0];
|
||||
} else {
|
||||
c[0] = c[3];
|
||||
c[1] = c[2];
|
||||
c[2] = env[2];
|
||||
c[3] = env[3];
|
||||
c[4] = c[0];
|
||||
}
|
||||
}
|
||||
|
||||
Polygon clipPoly = gf.createPolygon(c);
|
||||
try {
|
||||
Geometry subWarnArea = clip(clipPoly, warnArea);
|
||||
Geometry subFeatureGeom = clip(clipPoly, featureGeom);
|
||||
subdivIntersectInner(c, subWarnArea, subFeatureGeom,
|
||||
!orientation, depth + 1, subOut);
|
||||
} catch (TopologyException e) {
|
||||
// Additional fallback without clipping.
|
||||
statusHandler.handle(Priority.DEBUG,
|
||||
"Subdivided intersection failed. Will attempt fallback.", e);
|
||||
out.add(GeometryUtil.intersection(warnArea, featureGeom, true));
|
||||
return;
|
||||
}
|
||||
}
|
||||
out.addAll(subOut);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the intersection of p and g by operating on each element of g.
|
||||
* This is necessary to prevent "side location conflict" errors. By using
|
||||
* envelopes to either filter out elements or bypass
|
||||
* Geometry.intersection(), it also is much faster than p.intersection(g)
|
||||
* would be.
|
||||
*
|
||||
* @param p
|
||||
* @param g must be Polygonal
|
||||
* @return
|
||||
*/
|
||||
private static Geometry clip(Polygon p, Geometry g) {
|
||||
Envelope pe = p.getEnvelopeInternal();
|
||||
List<Polygon> out = new ArrayList<>(g.getNumGeometries() * 11 / 10);
|
||||
int n = g.getNumGeometries();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Geometry gi = g.getGeometryN(i);
|
||||
Envelope ge = gi.getEnvelopeInternal();
|
||||
if (pe.contains(ge)) {
|
||||
out.add((Polygon) gi);
|
||||
} else if (pe.intersects(ge)) {
|
||||
Geometry clipped = p.intersection(gi);
|
||||
int m = clipped.getNumGeometries();
|
||||
for (int j = 0; j < m; ++j) {
|
||||
Geometry gj = clipped.getGeometryN(j);
|
||||
if (!gj.isEmpty() && gj instanceof Polygon) {
|
||||
out.add((Polygon) gj);
|
||||
}
|
||||
}
|
||||
}
|
||||
// else, discard gi
|
||||
}
|
||||
return g.getFactory().createMultiPolygon(out.toArray(new Polygon[out.size()]));
|
||||
}
|
||||
|
||||
private static Geometry batchIntersect(Geometry warnArea, Geometry featureGeom) {
|
||||
return GeometryUtil.intersection(warnArea, featureGeom, true);
|
||||
}
|
||||
|
||||
public static List<String> converFeAreaToPartList(String feArea) {
|
||||
|
|
|
@ -153,6 +153,7 @@ import com.vividsolutions.jts.io.WKTReader;
|
|||
* Jul 15, 2015 DR17716 mgamazaychikov Change to Geometry class in total intersection calculations.
|
||||
* Oct 21, 2105 5021 randerso Fix issue with CORs for mixed case
|
||||
* Feb 9, 2016 DR18421 D. Friedman Don't call ToolsDataManager.setStormTrackData if there is no storm motion.
|
||||
* May 25, 2016 DR18789 D. Friedman Extract timezone calculation to method and add short circuit logic.
|
||||
* </pre>
|
||||
*
|
||||
* @author njensen
|
||||
|
@ -214,6 +215,127 @@ public class TemplateRunner {
|
|||
return officeCityTimezone;
|
||||
}
|
||||
|
||||
private static Set<String> determineTimezones(WarngenLayer warngenLayer,
|
||||
AffectedAreas[] areas, Geometry warningArea) throws VizException {
|
||||
Map<String, Double> intersectSize = new HashMap<String, Double>();
|
||||
double minSize = 1.0E-3d;
|
||||
Set<String> timeZones = new HashSet<String>();
|
||||
for (AffectedAreas affectedAreas : areas) {
|
||||
if (affectedAreas.getTimezone() != null) {
|
||||
// Handles counties that span two time zones
|
||||
String oneLetterTimeZones = affectedAreas.getTimezone()
|
||||
.trim();
|
||||
if (oneLetterTimeZones.length() == 1) {
|
||||
timeZones.add(String.valueOf(oneLetterTimeZones
|
||||
.charAt(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timeZones.size() > 1) {
|
||||
return timeZones;
|
||||
}
|
||||
for (AffectedAreas affectedAreas : areas) {
|
||||
if (affectedAreas.getTimezone() != null) {
|
||||
// Handles counties that span two time zones
|
||||
String oneLetterTimeZones = affectedAreas.getTimezone()
|
||||
.trim();
|
||||
if (oneLetterTimeZones.length() > 1) {
|
||||
// Determine if one letter timezone is going to be
|
||||
// put into timeZones.
|
||||
Geometry[] poly1, poly2;
|
||||
int n1, n2;
|
||||
double size, totalSize;
|
||||
String[] oneLetterTZ = new String[oneLetterTimeZones.length()];
|
||||
for (int i = 0; i < oneLetterTimeZones.length(); i++) {
|
||||
oneLetterTZ[i] = String
|
||||
.valueOf(oneLetterTimeZones.charAt(i));
|
||||
Geometry timezoneGeom = warngenLayer
|
||||
.getTimezoneGeom(oneLetterTZ[i]);
|
||||
long t0 = System.currentTimeMillis();
|
||||
poly1 = null;
|
||||
poly2 = null;
|
||||
n1 = 0;
|
||||
n2 = 0;
|
||||
size = 0.0d;
|
||||
totalSize = 0.0d;
|
||||
if ((timezoneGeom != null)
|
||||
&& (warningArea != null)) {
|
||||
if (intersectSize.get(oneLetterTZ[i]) != null) {
|
||||
continue;
|
||||
}
|
||||
poly1 = new Geometry[warningArea
|
||||
.getNumGeometries()];
|
||||
n1 = warningArea.getNumGeometries();
|
||||
for (int j = 0; j < n1; j++) {
|
||||
poly1[j] = warningArea.getGeometryN(j);
|
||||
}
|
||||
poly2 = new Geometry[timezoneGeom
|
||||
.getNumGeometries()];
|
||||
n2 = timezoneGeom.getNumGeometries();
|
||||
for (int j = 0; j < n2; j++) {
|
||||
poly2[j] = timezoneGeom.getGeometryN(j);
|
||||
}
|
||||
// Calculate the total size of intersection
|
||||
for (Geometry p1 : poly1) {
|
||||
for (Geometry p2 : poly2) {
|
||||
try {
|
||||
size = p1.intersection(p2)
|
||||
.getArea();
|
||||
} catch (TopologyException e) {
|
||||
statusHandler
|
||||
.handle(Priority.VERBOSE,
|
||||
"Geometry error calculating the total size of intersection.",
|
||||
e);
|
||||
}
|
||||
if (size > 0.0) {
|
||||
totalSize += size;
|
||||
}
|
||||
}
|
||||
if (totalSize > minSize) {
|
||||
break; // save time when the size of
|
||||
// poly1 or poly2 is large
|
||||
}
|
||||
}
|
||||
intersectSize
|
||||
.put(oneLetterTZ[i], totalSize);
|
||||
} else {
|
||||
throw new VizException(
|
||||
"Either timezoneGeom or/and warningArea is null. "
|
||||
+ "Timezone cannot be determined.");
|
||||
}
|
||||
perfLog.logDuration(
|
||||
"runTemplate size computation",
|
||||
System.currentTimeMillis() - t0);
|
||||
if (totalSize > minSize) {
|
||||
timeZones.add(oneLetterTZ[i]);
|
||||
}
|
||||
}
|
||||
// If timeZones has nothing in it when the hatched
|
||||
// area is very small,
|
||||
// use the timezone of larger intersection size.
|
||||
if (timeZones.size() == 0) {
|
||||
if (intersectSize.size() > 1) {
|
||||
if (intersectSize.get(oneLetterTZ[0]) > intersectSize
|
||||
.get(oneLetterTZ[1])) {
|
||||
timeZones.add(oneLetterTZ[0]);
|
||||
} else {
|
||||
timeZones.add(oneLetterTZ[1]);
|
||||
}
|
||||
} else {
|
||||
throw new VizException(
|
||||
"The size of intersectSize is less than 1, "
|
||||
+ "timezone cannot be determined.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new VizException(
|
||||
"Calling to area.getTimezone() returns null.");
|
||||
}
|
||||
}
|
||||
return timeZones;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a warngen template given the polygon from the Warngen Layer and
|
||||
* the Storm tracking information from StormTrackDisplay
|
||||
|
@ -317,113 +439,8 @@ public class TemplateRunner {
|
|||
context.put(ia, intersectAreas.get(ia));
|
||||
}
|
||||
|
||||
Map<String, Double> intersectSize = new HashMap<String, Double>();
|
||||
String[] oneLetterTZ;
|
||||
double minSize = 1.0E-3d;
|
||||
if ((areas != null) && (areas.length > 0)) {
|
||||
Set<String> timeZones = new HashSet<String>();
|
||||
for (AffectedAreas affectedAreas : areas) {
|
||||
if (affectedAreas.getTimezone() != null) {
|
||||
// Handles counties that span two time zones
|
||||
String oneLetterTimeZones = affectedAreas.getTimezone()
|
||||
.trim();
|
||||
oneLetterTZ = new String[oneLetterTimeZones.length()];
|
||||
if (oneLetterTimeZones.length() == 1) {
|
||||
timeZones.add(String.valueOf(oneLetterTimeZones
|
||||
.charAt(0)));
|
||||
} else {
|
||||
// Determine if one letter timezone is going to be
|
||||
// put into timeZones.
|
||||
Geometry[] poly1, poly2;
|
||||
int n1, n2;
|
||||
double size, totalSize;
|
||||
for (int i = 0; i < oneLetterTimeZones.length(); i++) {
|
||||
oneLetterTZ[i] = String
|
||||
.valueOf(oneLetterTimeZones.charAt(i));
|
||||
Geometry timezoneGeom = warngenLayer
|
||||
.getTimezoneGeom(oneLetterTZ[i]);
|
||||
t0 = System.currentTimeMillis();
|
||||
poly1 = null;
|
||||
poly2 = null;
|
||||
n1 = 0;
|
||||
n2 = 0;
|
||||
size = 0.0d;
|
||||
totalSize = 0.0d;
|
||||
if ((timezoneGeom != null)
|
||||
&& (warningArea != null)) {
|
||||
if (intersectSize.get(oneLetterTZ[i]) != null) {
|
||||
continue;
|
||||
}
|
||||
poly1 = new Geometry[warningArea
|
||||
.getNumGeometries()];
|
||||
n1 = warningArea.getNumGeometries();
|
||||
for (int j = 0; j < n1; j++) {
|
||||
poly1[j] = warningArea.getGeometryN(j);
|
||||
}
|
||||
poly2 = new Geometry[timezoneGeom
|
||||
.getNumGeometries()];
|
||||
n2 = timezoneGeom.getNumGeometries();
|
||||
for (int j = 0; j < n2; j++) {
|
||||
poly2[j] = timezoneGeom.getGeometryN(j);
|
||||
}
|
||||
// Calculate the total size of intersection
|
||||
for (Geometry p1 : poly1) {
|
||||
for (Geometry p2 : poly2) {
|
||||
try {
|
||||
size = p1.intersection(p2)
|
||||
.getArea();
|
||||
} catch (TopologyException e) {
|
||||
statusHandler
|
||||
.handle(Priority.VERBOSE,
|
||||
"Geometry error calculating the total size of intersection.",
|
||||
e);
|
||||
}
|
||||
if (size > 0.0) {
|
||||
totalSize += size;
|
||||
}
|
||||
}
|
||||
if (totalSize > minSize) {
|
||||
break; // save time when the size of
|
||||
// poly1 or poly2 is large
|
||||
}
|
||||
}
|
||||
intersectSize
|
||||
.put(oneLetterTZ[i], totalSize);
|
||||
} else {
|
||||
throw new VizException(
|
||||
"Either timezoneGeom or/and warningArea is null. "
|
||||
+ "Timezone cannot be determined.");
|
||||
}
|
||||
perfLog.logDuration(
|
||||
"runTemplate size computation",
|
||||
System.currentTimeMillis() - t0);
|
||||
if (totalSize > minSize) {
|
||||
timeZones.add(oneLetterTZ[i]);
|
||||
}
|
||||
}
|
||||
// If timeZones has nothing in it when the hatched
|
||||
// area is very small,
|
||||
// use the timezone of larger intersection size.
|
||||
if (timeZones.size() == 0) {
|
||||
if (intersectSize.size() > 1) {
|
||||
if (intersectSize.get(oneLetterTZ[0]) > intersectSize
|
||||
.get(oneLetterTZ[1])) {
|
||||
timeZones.add(oneLetterTZ[0]);
|
||||
} else {
|
||||
timeZones.add(oneLetterTZ[1]);
|
||||
}
|
||||
} else {
|
||||
throw new VizException(
|
||||
"The size of intersectSize is less than 1, "
|
||||
+ "timezone cannot be determined.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new VizException(
|
||||
"Calling to area.getTimezone() returns null.");
|
||||
}
|
||||
}
|
||||
Set<String> timeZones = determineTimezones(warngenLayer, areas, warningArea);
|
||||
|
||||
Map<String, String> officeCityTimezone = createOfficeTimezoneMap();
|
||||
String cityTimezone = null;
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.vividsolutions.jts.geom.prep.PreparedGeometry;
|
|||
* Apr 28, 2013 1955 jsanchez Added an ignoreUserData flag to intersection method.
|
||||
* Oct 21, 2013 DR 16632 D. Friedman Handle zero-length input in union.
|
||||
* Dec 13, 2013 DR 16567 Qinglu Lin Added contains().
|
||||
* May 12, 2016 DR 18789 D. Friedman Added intersection(g1, g2, ignoreUserData).
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -103,6 +104,26 @@ public class GeometryUtil {
|
|||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersection between g1 and g2. Resulting intersection will contain user
|
||||
* data from g2
|
||||
*
|
||||
* @param g1
|
||||
* @param g2
|
||||
*
|
||||
* @return the intersection between g1 and g2
|
||||
*/
|
||||
public static Geometry intersection(Geometry g1, Geometry g2, boolean ignoreUserData) {
|
||||
GeometryFactory gf = new GeometryFactory();
|
||||
List<Geometry> intersection = new ArrayList<Geometry>(
|
||||
g1.getNumGeometries() + g2.getNumGeometries());
|
||||
intersection(g1, g2, intersection, ignoreUserData);
|
||||
Geometry rval = gf.createGeometryCollection(intersection
|
||||
.toArray(new Geometry[intersection.size()]));
|
||||
rval.setUserData(g2.getUserData());
|
||||
return rval;
|
||||
}
|
||||
|
||||
private static void intersection(Geometry g1, Geometry g2,
|
||||
List<Geometry> intersections, boolean ignoreUserData) {
|
||||
if (g1 instanceof GeometryCollection) {
|
||||
|
|
Loading…
Add table
Reference in a new issue