Geocoding
Google Maps APIのGeocodingを使ってみる。
前回作った天気予報のRSSデータについて、地点名からGeocodingで経度・緯度を取得して表に整理する。
○RssPanel.java(前回作ったRSSPanelクラス)
import java.awt.BorderLayout; import java.awt.Component; import java.awt.Image; import java.awt.geom.Point2D; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import de.nava.informa.core.ChannelIF; import de.nava.informa.core.ItemIF; import de.nava.informa.core.ParseException; import de.nava.informa.impl.basic.ChannelBuilder; import de.nava.informa.parsers.FeedParser; public class RssPanel extends JPanel{ /** * */ private static final long serialVersionUID = 1L; private DefaultTableModel model; private Map<String,ImageIcon> icons; private final String apiKey="GoogleのAPIキー" public RssPanel(){ super(new BorderLayout()); JTable table=new JTable(); super.add(new JScrollPane(table),BorderLayout.CENTER); model=new DefaultTableModel(){ private static final long serialVersionUID = 1L; @Override public boolean isCellEditable(int row, int column) { return false; } }; model.setRowCount(0); model.setColumnCount(5); model.setColumnIdentifiers(new String[]{ "都市名","予報","URL","経度","緯度"}); table.setModel(model); table.setRowHeight(34); table.getColumnModel().getColumn(0).setMaxWidth(80); table.getColumnModel().getColumn(1).setMaxWidth(70); table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer(){ private static final long serialVersionUID = 1L; @Override public Component getTableCellRendererComponent( JTable arg0, Object arg1, boolean arg2, boolean arg3, int arg4, int arg5) { if(arg1 instanceof ImageIcon){ JLabel ll=new JLabel((ImageIcon)arg1); return ll; }else if(arg1 instanceof URL){ return super.getTableCellRendererComponent( arg0, arg1, arg2, arg3, arg4, arg5); }else{ return super.getTableCellRendererComponent( arg0, arg1, arg2, arg3, arg4, arg5); } } }); icons=new HashMap<String,ImageIcon>(); Runnable r=new Runnable(){ public void run(){ List<Entry> urls=updateAreaCity(); model.setRowCount(urls.size()); updateModel(urls); getLatLng(); } }; new Thread(r).start(); } private List<Entry> updateAreaCity(){ try{ List<Entry> ret=new ArrayList<Entry>(); DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance(); DocumentBuilder db=dbf.newDocumentBuilder(); URL url = new URL( "http://weather.livedoor.com/forecast/rss/forecastmap.xml"); Document doc=db.parse(url.openStream()); NodeList list=doc.getChildNodes(); Node n=list.item(0); parse(n,ret); return ret; }catch(Exception e){ e.printStackTrace(); return null; } } private void parse(Node n,List<Entry> ret){ NodeList list=n.getChildNodes(); for(int i=0;i<list.getLength();i++){ Node tmp=list.item(i); if(tmp.getNodeType()==3)continue; if(tmp.getNodeName().equals("city")){ NamedNodeMap map=tmp.getAttributes(); Entry e=new Entry(); e.id=Integer.parseInt( map.getNamedItem("id").getNodeValue()); e.name=map.getNamedItem("title").getNodeValue(); e.url=map.getNamedItem("source").getNodeValue(); ret.add(e); } if(tmp.hasChildNodes()){ parse(tmp,ret); } } } private void updateModel(List<Entry> urls){ for(Entry et : urls){ try{ URL url=new URL(et.url); ChannelIF channel = FeedParser.parse(new ChannelBuilder(), url); process(channel,et); }catch(MalformedURLException e){ e.printStackTrace(); }catch (ParseException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } } } private void getLatLng(){ GeoCoder gc=new GeoCoder(); for(int i=0;i<model.getRowCount();i++){ String name=(String)model.getValueAt(i, 0); GeoCoderEntry[] e=gc.geoCode(name, apiKey); if(e.length>0){ Point2D c=e[0].getCoordinates(); model.setValueAt(Double.toString(c.getX()),i,3); model.setValueAt(Double.toString(c.getY()),i,4); } try{Thread.sleep(300);}catch(Exception ex){} } } private void process(ChannelIF channel,Entry e) throws ParseException,IOException{ Set<ItemIF> list=channel.getItems(); for(ItemIF item : list){ if(item.getTitle().contains("PR"))continue; URL url=item.getLink(); String is=item.getElementValues( "image", new String[]{"url"})[0]; ImageIcon icon=icons.get(is); if(icon==null){ URL u=new URL(is); Image im=ImageIO.read(u); icon=new ImageIcon(im); } model.setValueAt(e.name, e.id-1, 0); model.setValueAt(icon, e.id-1, 1); model.setValueAt(url, e.id-1, 2); break; } } private class Entry{ int id; String name; String url; } public static void main(String[] args) { JFrame f=new JFrame(); try { UIManager.setLookAndFeel( "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); SwingUtilities.updateComponentTreeUI(f); }catch(Exception e){} f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().setLayout(new BorderLayout()); f.setSize(480, 320); f.getContentPane().add(new RssPanel(),BorderLayout.CENTER); f.setVisible(true); } }
○GeoCoder.java(Geocodingで緯度・経度を取得するクラス)
import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class GeoCoder { private String proxy; private int port; public GeoCoder(){ proxy=null; } public GeoCoder(String proxy,int port){ this.proxy=proxy; this.port=port; } public GeoCoderEntry[] geoCode(String str,String apiKey){ List<GeoCoderEntry> list=new ArrayList<GeoCoderEntry>(); try{ String urls="http://maps.google.com/maps/geo?"; String query="q="+str+"&output=xml&oe=utf8&sensor=true_or_false&key="+apiKey; URL url; if(proxy==null){ url=new URL(urls+query); }else{ url=new URL("http",proxy,port,urls+query); } HttpURLConnection conn=(HttpURLConnection)url.openConnection(); conn.setRequestMethod("GET"); conn.setInstanceFollowRedirects(false); conn.setRequestProperty("Accept-Language", "ja;q=0.7,en;q=0.3"); conn.connect(); DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance(); DocumentBuilder db=dbf.newDocumentBuilder(); Document doc=db.parse(conn.getInputStream()); parse(doc,list); try{Thread.sleep(500);}catch(Exception e){} }catch(MalformedURLException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }catch(ParserConfigurationException e){ e.printStackTrace(); }catch(SAXException e){ e.printStackTrace(); } return list.toArray(new GeoCoderEntry[list.size()]); } private void parse(Document doc,List<GeoCoderEntry> l){ NodeList list=doc.getChildNodes(); for(int i=0;i<list.getLength();i++){ Node node=list.item(i); parseNode(node,l); } } private void parseNode(Node n,List<GeoCoderEntry> l){ if(n.getNodeName().equals("Placemark")){ l.add(new GeoCoderEntry(n)); }else{ NodeList list=n.getChildNodes(); for(int i=0;i<list.getLength();i++){ Node node=list.item(i); parseNode(node,l); } } } }
○GeoCoderEntry.java
import java.awt.geom.Point2D; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class GeoCoderEntry { private String id; private String address; private String contryName; private String administrativeAreaName; private String addressLine; private Point2D coordinates; private Point2D[] rectNESW; public GeoCoderEntry(Node n)throws IllegalArgumentException{ if(!n.getNodeName().equals("Placemark")){ throw new IllegalArgumentException("Not Placemark Node"); } NamedNodeMap nnp=n.getAttributes(); Node p=nnp.getNamedItem("id"); id=p.getNodeValue(); NodeList list=n.getChildNodes(); for(int i=0;i<list.getLength();i++){ parseNode(list.item(i)); } } private void parseNode(Node n){ String name=n.getNodeName(); if(name.equals("address")){ address=n.getTextContent(); }else if(name.equals("CountryName")){ contryName=n.getTextContent(); }else if(name.equals("AdministrativeAreaName")){ administrativeAreaName=n.getTextContent(); }else if(name.equals("AddressLine")){ addressLine=n.getTextContent(); }else if(name.equals("LatLonBox")){ NamedNodeMap nnp=n.getAttributes(); String norths=nnp.getNamedItem("north").getNodeValue(); String souths=nnp.getNamedItem("south").getNodeValue(); String easts=nnp.getNamedItem("east").getNodeValue(); String wests=nnp.getNamedItem("west").getNodeValue(); rectNESW=new Point2D[2]; rectNESW[0]=new Point2D.Double( Double.parseDouble(norths), Double.parseDouble(easts) ); rectNESW[1]=new Point2D.Double( Double.parseDouble(souths), Double.parseDouble(wests) ); }else if(name.equals("coordinates")){ String[] ll=n.getTextContent().split(","); double lat=Double.parseDouble(ll[0]); double lng=Double.parseDouble(ll[1]); coordinates=new Point2D.Double(lat,lng); } NodeList list=n.getChildNodes(); for(int i=0;i<list.getLength();i++){ parseNode(list.item(i)); } } public String getId() { return id; } public String getAddress() { return address; } public String getContryName() { return contryName; } public String getAdministrativeAreaName() { return administrativeAreaName; } public String getAddressLine() { return addressLine; } public Point2D getCoordinates() { return coordinates; } public Point2D[] getRectNESW() { return rectNESW; } public String getOutline(){ return "<"+address+">"+coordinates.getX()+","+coordinates.getY(); } }
■実行結果
とりあえず、天気予報について、地名、予報、緯度・経度が取得できたので、次は、ちょっと簡単なマッシュアップってやつをやってみたい。