/**
* Copyright tepe ( http://wonderfl.net/user/tepe )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/eVXO
*/
<?xml version="1.0" encoding="utf-8"?>
<!-- forked from Fake's forked from: forked from: Google Maps API test -->
<!-- forked from Fake's forked from: Google Maps API test -->
<!-- forked from Fake's Google Maps API test -->
<!--
RTMFPによるマルチプレイヤー対応版
色々と実装途中
Google Maps APIがproprietaryなので、GPLに変更出来ない。
テストコードからforkしてるので、ライセンスが変更出来ない。
自分のコードからforkしてるのにライセンスが変更出来ないのはおかしいんだけど…。
上記を踏まえた上で、
wonderfl上の表示では、All rights reserved になっていますが、ライセンスは CDDL にします。
I provide this code as CDDL.
English:
http://opensource.org/licenses/cddl1.php
日本語訳
http://sourceforge.jp/projects/opensource/wiki/licenses%2FCommon_Development_and_Distribution_License
FAQ(日本語)
http://hub.opensolaris.org/bin/view/Project+jp/licensing_faq
Copyright (c) 2010 Fake
以下、簡単にCDDLの説明:
CDDLは、コピーレフト的にはLGPLより更に弱いものとなってますので、
静的/動的に関わらずリンクされるライブラリに対してのソース公開を要求しません。
但し、本ソースコードに関してはコピーレフトとなりますので、ソースの公開が必要となります。
wonderfl内でのforkに関しては、自動的にソースが公開されますので、特に気にする必要はありません。
2010/11/05 Fake クライアントのIPを元に初期位置を決めるようにした。
-->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<maps:Map xmlns:maps="com.google.maps.*" id="map" mapevent_mapready="onMapReady(event)" x="0" y="20"
width="100%" height="100%" key="ABQIAAAALF5oQPABFhnvOj2NwrgHKhSO5wqdqdHVqPB8sVJhBj86bhrVXRQGr4mbD7pCuDIQokA-caR3k3Xn1Q"/>
<mx:Script>
<![CDATA[
import com.google.maps.LatLng;
import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.MapType;
import com.google.maps.overlays.*;
import com.google.maps.styles.*;
import com.google.maps.InfoWindowOptions;
import com.google.maps.MapAction;
import com.google.maps.MapMouseEvent;
import com.google.maps.services.*;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.NetStatusEvent;
import flash.net.*;
private var debugout:TextField;
private var marker:Marker;
private var myicon:Loader;
private var mypt:Point;
private var moveTarget:Point;
private var MOVE_STEP:Number = 0.001;
private var cmdline:TextField;
private var balloon:TextField;
private var iconurl:String;
private var moveTimer:Timer;
private var sendTimer:Timer;
private var nodes:Object;
private var gs:GroupSpecifier;
private var nc:NetConnection;
private var group:NetGroup;
private var CMD_MOVE:String = "move";
private var CMD_ICON:String = "icon";
private var CMD_DISCO:String = "disco";
private function onMapReady(event:Event):void {
debugout = new TextField();
debugout.x = 50;
debugout.y = 50;
debugout.autoSize = TextFieldAutoSize.LEFT;
debugout.background = false;
debugout.border = true;
debugout.backgroundColor = 0x000000;
debugout.textColor = 0x0000FF;
//parent.addChild(debugout);
debugout.text = "hoge";
// init rtmfp group spec
gs = new GroupSpecifier("net.wonderfl.fake.wonderflworld.test");
gs.postingEnabled = true;
gs.ipMulticastMemberUpdatesEnabled = true;
gs.multicastEnabled = true;
gs.objectReplicationEnabled = true;
gs.routingEnabled = true;
gs.serverChannelEnabled = true;
// init rtmfp connection
nc = new NetConnection();
nc.addEventListener(NetStatusEvent.NET_STATUS, procNetStatus);
nc.connect("rtmfp://p2p.rtmfp.net/445d9931dfac67103408ae5f-ad98e28e2386/");
nodes = new Object();
}
private function initWorld(latlng:LatLng = null):void {
if (latlng == null) {
latlng = new LatLng(40.736072,-73.992062);
}
this.map.disableDragging();
this.map.disableContinuousZoom();
this.map.disableControlByKeyboard();
this.map.disableScrollWheelZoom();
this.map.setDoubleClickMode(MapAction.ACTION_NOTHING);
this.map.setCenter(latlng, 14, MapType.NORMAL_MAP_TYPE);
myicon = new Loader();
myicon.contentLoaderInfo.addEventListener(Event.COMPLETE, onIconLoaded);
myicon.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIconLoadError);
iconurl = "http://maps.google.co.jp/mapfiles/ms/icons/red-dot.png";
myicon.load(new URLRequest(iconurl), new LoaderContext(true));
marker = new Marker(latlng, new MarkerOptions({
icon : myicon
}));
this.map.addOverlay(marker);
//marker.openInfoWindow(new InfoWindowOptions({content: "I am Omochi."}));
cmdline = new TextField();
cmdline.x = 0;
cmdline.y = 0;
cmdline.width = 200;
cmdline.height = 20;
cmdline.border = true;
cmdline.type = TextFieldType.INPUT;
cmdline.multiline = false;
cmdline.maxChars = 140;
cmdline.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDownCmdline);
parent.addChild(cmdline);
balloon = new TextField();
balloon.border = true;
balloon.background = true;
balloon.backgroundColor = 0xFFFFFF;
balloon.textColor = 0x000000;
balloon.width = 200;
balloon.height = 20;
balloon.autoSize = TextFieldAutoSize.LEFT;
balloon.wordWrap = true;
balloon.text = "guest";
var p:Point = map.fromLatLngToViewport(latlng);
balloon.x = p.x - 100;
balloon.y = p.x - 30;
parent.addChild(balloon);
this.map.addEventListener(MapMouseEvent.MOUSE_DOWN, onMouseClickMap);
myicon.addEventListener(MouseEvent.CLICK, onMouseClick);
mypt = new Point(latlng.lng(), latlng.lat());
moveTarget = mypt.clone();
moveTimer = new Timer(200, 0);
moveTimer.addEventListener(TimerEvent.TIMER, procMoveTimer);
moveTimer.start();
sendTimer = new Timer(5000, 0);
sendTimer.addEventListener(TimerEvent.TIMER, procSendTimer);
sendTimer.start();
}
private function onIconLoaded(e:Event):void {
var loader:Loader = e.target.loader;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onIconLoaded);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onIconLoadError);
loader.width = 32;
loader.height = 32;
}
private function onIconLoadError(e:Event):void {
//marker.openInfoWindow(new InfoWindowOptions({content: "icon load error"}));
var loader:Loader = e.target.loader;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onIconLoaded);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onIconLoadError);
iconurl = "http://maps.google.co.jp/mapfiles/ms/icons/red-dot.png";
loader.scaleX = 1.0;
loader.scaleY = 1.0;
loader.load(new URLRequest(iconurl), new LoaderContext(true));
}
private function onMouseClickMap(e:MapMouseEvent):void {
moveTarget.x = e.latLng.lng();
moveTarget.y = e.latLng.lat();
}
private function onMouseClick(e:MouseEvent):void {
//marker.openInfoWindow(new InfoWindowOptions({content: e.toString()}));
}
private function procSendTimer(e:TimerEvent):void {
group.post({
cmd: CMD_MOVE,
id: nc.nearID,
point: mypt,
label: balloon.text,
iconurl: iconurl
});
}
private function procMoveTimer(e:TimerEvent):void {
var latlng:LatLng;
var p:Point;
// draw others
for (var id:String in nodes) {
if (id in nodes) {
var node:Object = nodes[id];
latlng = new LatLng(node.point.y, node.point.x);
node.marker.setLatLng(latlng);
/*
if (node.label && map.getLatLngBounds().containsLatLng(latlng)) {
//node.marker.openInfoWindow(new InfoWindowOptions({content: node.label}));
} else {
//node.marker.closeInfoWindow();
}
*/
p = map.fromLatLngToViewport(latlng);
node.balloon.x = p.x - 100;
node.balloon.y = p.y - 30;
}
}
// draw self
if (moveTarget.equals(mypt)) {
return;
}
if (Point.distance(moveTarget, mypt) <= MOVE_STEP) {
mypt = moveTarget.clone();
} else {
p = moveTarget.subtract(mypt);
p.normalize(MOVE_STEP);
mypt.offset(p.x, p.y);
}
group.post({
cmd: CMD_MOVE,
id: nc.nearID,
point: mypt,
label: null,
iconurl: iconurl
});
latlng = new LatLng(mypt.y, mypt.x);
this.map.setCenter(latlng, 14, MapType.NORMAL_MAP_TYPE);
marker.setLatLng(latlng);
p = map.fromLatLngToViewport(latlng);
balloon.x = p.x - 100;
balloon.y = p.x - 30;
//marker.openInfoWindow(new InfoWindowOptions({content: latlng.toString()}));
}
private function onKeyDownCmdline(e:KeyboardEvent):void {
if (e.keyCode == Keyboard.ENTER && cmdline.text != "") {
var r:RegExp = /^\/([a-z]+) +(.*)$/g;
var res:Object = r.exec(cmdline.text);
if (res == null) {
group.post({
cmd: CMD_MOVE,
id: nc.nearID,
point: mypt,
label: cmdline.text
});
//marker.openInfoWindow(new InfoWindowOptions({content: cmdline.text}));
balloon.text = cmdline.text;
cmdline.text = "";
return;
}
switch (res[1]) {
case "icon":
myicon.contentLoaderInfo.addEventListener(Event.COMPLETE, onIconLoaded);
myicon.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIconLoadError);
iconurl = res[2];
myicon.load(new URLRequest(iconurl), new LoaderContext(true));
break;
case "tp":
var ar:Array = res[2].split(/, */, 2);
var x:Number = parseFloat(ar[0]);
var y:Number = parseFloat(ar[1]);
if (isNaN(x) || isNaN(y)) {
var geoc:ClientGeocoder = new ClientGeocoder();
geoc.addEventListener(GeocodingEvent.GEOCODING_SUCCESS, onGeocodeSuccess);
geoc.geocode(res[2]);
break;
}
moveTarget.y = x;
moveTarget.x = y;
mypt = moveTarget.clone();
mypt.x += 0.0001
break;
/*
case "move":
var geoc:ClientGeocoder = new ClientGeocoder();
geoc.addEventListener(GeocodingEvent.GEOCODING_SUCCESS, onGeocodeSuccess);
geoc.geocode(res[2]);
break;
*/
default:
//marker.openInfoWindow(new InfoWindowOptions({content: "unknown command"}));
break;
}
cmdline.text = "";
}
}
private function onGeocodeSuccess(e:GeocodingEvent):void {
if (e.response.placemarks.length == 0) {
return;
}
var latlng:LatLng = e.response.placemarks[0].point;
//marker.openInfoWindow(new InfoWindowOptions({content: latlng.toString()}));
moveTarget.y = latlng.lat();
moveTarget.x = latlng.lng();
mypt = moveTarget.clone();
mypt.x += MOVE_STEP * 0.1;
}
private function procNetStatus(e:NetStatusEvent):void {
//debugout.appendText(e.info.code + "\n");
var node:Object;
switch (e.info.code) {
case "NetConnection.Connect.Success":
group = new NetGroup(nc, gs.groupspecWithAuthorizations());
group.addEventListener(NetStatusEvent.NET_STATUS, procNetStatus);
break;
case "NetGroup.Connect.Success":
var urlloader:URLLoader = new URLLoader();
urlloader.addEventListener(Event.COMPLETE, function (e:Event):void {
var xml:XML = new XML(e.target.data);
initWorld(new LatLng(parseFloat(xml.latitude), parseFloat(xml.longitude)));
})
urlloader.addEventListener(IOErrorEvent.IO_ERROR, function (e:Event):void {
initWorld();
});
urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
initWorld();
});
urlloader.load(new URLRequest("http://www.onflex.org/geo/xml/"));
break;
case "NetGroup.Neighbor.Connect":
break;
case "NetGroup.Neighbor.Disconnect":
// send notice to group when neighbor disconnected
if (e.info.peerID in nodes) {
map.removeOverlay(nodes[e.info.peerID].marker);
parent.removeChild(nodes[e.info.peerID].balloon);
delete nodes[e.info.peerID];
group.post({
cmd: CMD_DISCO,
id: e.info.peerID
});
}
break;
case "NetGroup.Posting.Notify":
var obj:Object = e.info.message;
switch (obj.cmd) {
case CMD_MOVE:
if (!(obj.id in nodes)) {
nodes[obj.id] = new Object();
node = nodes[obj.id];
node.icon = new Loader();
//node.icon.node = node;
node.iconurl = null;
node.marker = new Marker(new LatLng(obj.point.y, obj.point.x), new MarkerOptions({
strokeStyle: new StrokeStyle({color: 0x987654}),
fillStyle: new FillStyle({color: 0x223344, alpha: 0.8}),
radius: 12,
hasShadow: true
}));
map.addOverlay(node.marker);
node.point = new Point();
node.balloon = new TextField();
node.balloon.border = true;
node.balloon.background = true;
node.balloon.backgroundColor = 0xFFFFFF;
node.balloon.textColor = 0x000000;
node.balloon.width = 200;
node.balloon.height = 20;
node.balloon.autoSize = TextFieldAutoSize.LEFT;
node.balloon.wordWrap = true;
parent.addChild(node.balloon);
node.label = null;
}
node = nodes[obj.id];
node.point.x = obj.point.x;
node.point.y = obj.point.y;
if (obj.label != null) {
node.label = new String(obj.label);
node.balloon.text = new String(node.label);
}
if (obj.iconurl && obj.iconurl != node.iconurl) {
node.icon.contentLoaderInfo.addEventListener(Event.COMPLETE, onIconLoaded);
//node.icon.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIconLoadError);
node.iconurl = obj.iconurl;
node.icon.load(new URLRequest(node.iconurl), new LoaderContext(true));
node.marker.setOptions(new MarkerOptions({
icon : node.icon
}));
}
break;
case CMD_ICON:
break;
case CMD_DISCO:
if (obj.id in nodes) {
map.removeOverlay(nodes[obj.id].marker);
parent.removeChild(nodes[obj.id].balloon);
delete nodes[obj.id];
}
break;
}
break;
}
}
]]>
</mx:Script>
</mx:Application>