/* * The MIT License (MIT) * * Copyright (c) 2014 México Abierto * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * For more information visit https://github.com/mxabierto/avisos. */ package mx.org.cedn.avisosconagua.mongo; import com.google.publicalerts.cap.Alert; import com.google.publicalerts.cap.Area; import com.google.publicalerts.cap.CapValidator; import com.google.publicalerts.cap.CapXmlBuilder; import com.google.publicalerts.cap.Circle; import com.google.publicalerts.cap.Group; import com.google.publicalerts.cap.Info; import com.google.publicalerts.cap.Point; import com.google.publicalerts.cap.Polygon; import com.google.publicalerts.cap.ValuePair; import com.mongodb.BasicDBObject; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import mx.org.cedn.avisosconagua.util.Utils; /** * Utility class to generate a CAP representation of an advice stores in a MongoDB object. * @author serch */ public class CAPGenerator { /** ID of the current advice **/ private final String currentId; /** MongoDB object to gather information from the whole advice **/ private final BasicDBObject aviso; /** MongoDB object to gather information from the main section of the advice **/ private final BasicDBObject init; /** MongoDB object to gather information from the initial information section of the advice **/ private final BasicDBObject precapture; // private final BasicDBObject pronostico; // private final BasicDBObject seguimiento; /** MongoDB object to gather information from the issue information section of the advice **/ private final BasicDBObject capInfo; /** Issue date **/ private String _date = null; /** * Constructor. Creates a new instance of a CAPGenerator with the given ID. * @param currentId ID of the current advice. */ public CAPGenerator(String currentId) { this.currentId = currentId; aviso = MongoInterface.getInstance().getAdvice(currentId); init = (BasicDBObject) aviso.get("init"); // pronostico = (BasicDBObject) aviso.get("pronostico"); // seguimiento = (BasicDBObject) aviso.get("seguimiento"); capInfo = (BasicDBObject) aviso.get("capInfo"); precapture = (BasicDBObject) aviso.get("precapture"); } /** * Generates the XML representation of the advice in CAP format. * @return CAP XML for the advice. */ public String generate() { return getXML(); } /** * Gets the issue date of the CAP. * @return issue date. */ public String getDate() { return _date; } /** * Creates a {@link com.google.publicalerts.cap.Alert} object from the CAP representation of the advice. * @return {@link com.google.publicalerts.cap.Alert} object. */ public Alert generateAlert() { return getValidAlertBuilder().build(); } /** * Generates the XML representation of the advice in CAP format. * @return CAP XML */ private String getXML() { CapXmlBuilder builder = new CapXmlBuilder(); return builder.toXml(getValidAlertBuilder()); } /** * Replaces new line characters with carriage returns in a given string. * @param origin source string * @return final string with only carriage returns */ private String removeLF(String origin) { return origin.replaceAll("\n", " ").replaceAll("\r", ""); } /** * Gets a valid {@link com.google.publicalerts.cap.Alert.Builder} from the Alert object. * @return valid {@link com.google.publicalerts.cap.Alert.Builder} */ private Alert.Builder getValidAlertBuilder() { Alert.Status status = Alert.Status.ACTUAL; String updateToId = null; if (null != precapture) { String capStatus = precapture.getString("capStatus"); if ("actual".equals(capStatus)) { status = Alert.Status.ACTUAL; } if ("test".equals(capStatus)) { status = Alert.Status.TEST; } if ("excercise".equals(capStatus)) { status = Alert.Status.EXERCISE; } if ("system".equals(capStatus)) { status = Alert.Status.SYSTEM; } if ("draft".equals(capStatus)) { status = Alert.Status.DRAFT; } updateToId = precapture.getString("previousIssue"); } _date = capInfo.getString("issueDate") + " " + capInfo.getString("issueTime"); String sender = "smn.cna.gob.mx"; Alert.Builder builder = Alert.newBuilder() .setXmlns(CapValidator.CAP12_XMLNS) .setIdentifier(currentId) .setSender(sender) .setSent(Utils.getISODate(_date))//2003-04-02T14:39:01-05:00 2014-04-93T12:58:00-06:00 .setStatus(status) //Alert.Status.ACTUAL .setScope(Alert.Scope.PUBLIC) .addCode(capInfo.getString("issueNumber")); // // .setIncidents(Group.newBuilder() // .addValue("incident1").addValue("incident2").build()) Alert.MsgType type = Alert.MsgType.ALERT; // Definir si ACTUAL o UPDATE if (null != updateToId && !"".equals(updateToId.trim())) { type = Alert.MsgType.UPDATE; BasicDBObject ci = (BasicDBObject) MongoInterface.getInstance().getAdvice(updateToId).get("capInfo"); String fecha = ci.getString("issueDate") + " " + ci.getString("issueTime"); String preSent = Utils.getISODate(fecha); builder.setReferences(Group.newBuilder() .addValue(sender + "," + updateToId + "," + preSent) .build()); } builder.setMsgType(type); /* for (HashMap<String, String> event : listEvents) { builder.addInfo(getValidInfoBuilder(event)); }*/ HashMap<String, String> areas = new HashMap<String, String>(); for (String key : init.keySet()) { if (key.startsWith("area-")) { areas.put(key.substring(5), init.getString(key)); } } builder.addInfo(getValidInfoBuilder(areas)); return builder; } /** * Gets a valid {@link com.google.publicalerts.cap.Info.Builder} from the advice info, using area information. * @param areaId ID of an area of the google map. * @return valid {@link com.google.publicalerts.cap.Info.Builder} */ private Info.Builder getValidInfoBuilder(HashMap<String, String> areas) { String disclaimer = "Los datos contenidos en el presente documento, forman parte de un producto piloto en fase experimental para avisar únicamente sobre ciclones tropicales. Dichos datos se determinaron con base en la información de modelos numéricos de fenómenos hidrometeorológicos con los que cuenta la Comisión Nacional del Agua, por lo que al ser los mismos variables, no es posible determinar con exactitud su ocurrencia y magnitud. Las medidas para la prevención y mitigación de sus efectos son emitidas por las autoridades de protección civil."; String datea = capInfo.getString("issueDate") + " " + capInfo.getString("issueTime"); String dateb = capInfo.getString("issueNextDate") + " " + capInfo.getString("issueNextTime"); Info.Urgency urg = Info.Urgency.UNKNOWN_URGENCY; Info.Severity sev = Info.Severity.UNKNOWN_SEVERITY; Info.Certainty cer = Info.Certainty.UNKNOWN_CERTAINTY; Info.Builder builder = Info.newBuilder() .setLanguage("es-419") .addCategory(Info.Category.MET) .setEvent(Utils.getTituloBoletin(aviso.getString("adviceType"))) .setUrgency(urg) .setSeverity(sev) .setCertainty(cer) .setEffective(Utils.getISODate(datea)) .setExpires(Utils.getISODate(dateb)) .addResponseType(Info.ResponseType.EXECUTE) .setSenderName("Comisión Nacional del Agua - Servicio Meteorológico Nacional") .setHeadline(capInfo.getString("eventHeadline")) .setDescription(removeLF(init.getString("eventDescription")) + "\n\n" + disclaimer) .setWeb("http://smn.cna.gob.mx/") // .addParameter(ValuePair.newBuilder() // .setValueName("HSAS").setValue("ORANGE").build()) // .setAudience("an audience") .setContact("webmaster@conagua.gob.mx") // .addEventCode(ValuePair.newBuilder() // .setValueName("EC").setValue("v1").build()) // .setEffective("2003-04-02T14:39:01-05:00") // .setOnset("2003-04-02T15:39:01+05:00") // .setExpires("2003-04-02T16:39:01-00:00") .addParameter(ValuePair.newBuilder() .setValueName("Elaboró").setValue(capInfo.getString("issueMetheorologist")).build()) .addParameter(ValuePair.newBuilder() .setValueName("Revisó").setValue(capInfo.getString("issueShiftBoss")).build()); if (null != init.getString("eventRisk") && !"".equals(init.getString("eventRisk"))) { builder.addParameter(ValuePair.newBuilder().setValueName("semáforo").setValue(init.getString("eventRisk")).build()); } if (null != init.getString("eventInstructions")) { builder.setInstruction(init.getString("eventInstructions")); } /* if (winds <= 62) category = "Depresión Tropical"; if (winds >= 62.1 && winds <= 118) category = "Tormenta Tropical"; if (winds >= 118.1 && winds <= 154) category = "Huracán categoría I"; if (winds >= 154.1 && winds <= 178) category = "Huracán categoría II"; if (winds >= 178.1 && winds <= 210) category = "Huracán categoría III"; if (winds >= 210.1 && winds <= 250) category = "Huracán categoría IV"; if (winds >= 250.1) category = "Huracán categoría V"; */ String category = null; try { float winds = -1f; winds = Float.parseFloat(init.getString("eventWindSpeedSust")); if (winds > -1f && winds <= 62f) { category = "Depresión Tropical"; } if (winds >= 62.1f && winds <= 118f) { category = "Tormenta Tropical"; } if (winds >= 118.1f && winds <= 154f) { category = "Huracán categoría I"; } if (winds >= 154.1f && winds <= 178f) { category = "Huracán categoría II"; } if (winds >= 178.1f && winds <= 210f) { category = "Huracán categoría III"; } if (winds >= 210.1f && winds <= 250f) { category = "Huracán categoría IV"; } if (winds >= 250.1f) { category = "Huracán categoría V"; } } catch (NumberFormatException noe) { } if (null != category) { builder.addParameter(ValuePair.newBuilder().setValueName("categoría").setValue(category).build()); } builder.addParameter(ValuePair.newBuilder().setValueName("distancia").setValue(init.getString("eventCoastDistance")).build()); //Add <area> to <info> (if any). if (!areas.isEmpty()) { HashSet<String> statesSet = new HashSet<String>(); Area.Builder areaBuilder = Area.newBuilder(); for (String areaId : areas.keySet()) { String[] aarea = areas.get(areaId).split(" "); if (aarea[aarea.length - 1].indexOf(",") > 0) { areaBuilder.addPolygon(getPolygonArea(aarea)); } else { areaBuilder.addCircle(getCircleArea(aarea)); } String tstates = init.getString("states-" + areaId); if (null != tstates) { String []sstates = tstates.split(","); if (sstates != null && sstates.length > 0) { statesSet.addAll(Arrays.asList(sstates)); } } } String states = ""; if (!statesSet.isEmpty()) { Iterator<String> sit = statesSet.iterator(); while (sit.hasNext()) { String t = sit.next(); states += t; if (sit.hasNext()) { states+= ","; } } } areaBuilder.setAreaDesc(states); builder.addArea(areaBuilder); } String trayectoria = removeLF(init.getString("eventDistance")); if (null != trayectoria) { builder.addParameter(ValuePair.newBuilder().setValueName("trayectoria").setValue(trayectoria).build()); } String regionName = Utils.getRegionFromAdviceType(aviso.getString("adviceType")); builder.addParameter(ValuePair.newBuilder().setValueName("layer:Google:Region:0.1").setValue(regionName).build()); return builder; } /** * Gets an {@link com.google.publicalerts.cap.Polygon.Builder} object for a polygon. * @param area array of polygon points * @return {@link com.google.publicalerts.cap.Area.Builder} object */ private Polygon.Builder getPolygonArea(String[] area) { Polygon.Builder poli = Polygon.newBuilder(); for (String coord : area) { String[] scord = coord.split(","); poli.addPoint(Point.newBuilder().setLatitude(Double.parseDouble(scord[0])).setLongitude(Double.parseDouble(scord[1])).build()); } return poli; } /** * Gets an {@link com.google.publicalerts.cap.Circle.Builder} object for a circular area. * @param area Coordinates and radius of the area * @return {@link com.google.publicalerts.cap.Area.Builder} object */ private Circle.Builder getCircleArea(String[] area) { double latitude, logitude, radius; String[] scord = area[0].split(","); latitude = Double.parseDouble(scord[0]); logitude = Double.parseDouble(scord[1]); radius = Double.parseDouble(area[1]); return Circle.newBuilder() .setPoint(Point.newBuilder().setLatitude(latitude).setLongitude(logitude).build()) .setRadius(radius); } }