2015年6月9日 星期二

蘋果派

所有材料,蘋果、檸檬、糖、肉桂粉、蘭姆酒、奶油

蘋果削皮,削皮超難的

全部切片,邊聽新聞邊切,八顆蘋果切出來一大鍋蘋果片

一大~鍋蘋果片

先溶化奶油,融化之後丟糖

之後蘋果片全部丟進去炒,蘋果片有水也沒關係

順便加點蘭姆酒

炒出水後要持續攪拌來燉煮

炒到很軟,蘋果片也變色的時候再放一點點肉桂粉就好,一點點就好

我炒到最後變這樣,很軟很好吃,裝盤冷卻一下,順便把派皮拿出來

開始鋪蘋果片,派皮這次偷懶買現成的

把蘋果片鋪完

第一次做抓不準量,蘋果片少了一點

先用200度烤20分鐘,再用150度烤10分鐘

很香!!!!

成品,廚房光線不是很好,本人比較亮一點很漂亮,超香


海鮮燉飯配羊排

海鮮燉飯!
上次做是去年萬聖節,已經是半年前了,整個功力有進步啊~
特別一提,這次羊排是去南門市場買的紐西蘭小羔羊
非常好吃,很嫩很嫩很嫩,羊騷味也不會太重,抹鹽抹橄欖油直接烤就好吃到歪掉
不過一包8隻要580,但滿值得的

BTW,本次有好友攝影師A(歡呼)

材料
磨菇一盒約80g,切片
正常大小的洋蔥一顆,切丁 
拳頭大的馬鈴薯,切丁
大蒜五瓣,切末
雞心辣椒一根,切段
海鮮湯,偷雞用現成的,一碗
台灣米3杯,洗兩次,泡30分鐘
小花枝一盒120g兩盒
蟹肉80g一盒
蝦仁一盒,多重忘記看了

特寫肉跟馬鈴薯丁(?)

先炒洋蔥、大蒜、辣椒

洋蔥透明後放磨菇跟海鮮

海鮮很會出水,出水後加香料,這次用月桂葉、百里先、羅勒葉,覺得羅勒很搭燉飯


花枝變白變硬後放米,倒湯

不斷重複加湯=>收汁的步驟,這次米先泡水,效果很好,不但比較快熟,而且也比較好煮跟好入味,整鍋燉飯不算備料的時間1小時就做好了,上次花了快3小時在煮飯XD

成品,攝影師覺得這邊該打燈這樣

很嫩很嫩的羊排

 買的時候老闆一直叮嚀只要抹鹽就好,不要過度調味
真的一直強調,所以應該要聽一下
我先抹了薄薄一層橄欖油,然後灑鹽

烤箱先預熱,烤160度,15分鐘

顏色很漂亮,香味很重

 很漂亮,放15分鐘讓肉休息收汁

最後補一張,手機拍的,顏色小暗淡一點
燉飯或許是用了鑄鐵鍋比較好煮,或許是米有泡水,過程快非常多
而且一氣呵成,流利許多
海鮮味跟微微的辣很讚
羊排非常軟嫩,或許可以再多烤一下下或是多一點點油
咬開肉汁重擊舌頭整個都高潮了

2015年3月31日 星期二

驗證GOOGLE轉回來的經緯度正確與否

手上的專案一直都有把地址轉成經緯度,不過由於GOOGLE並沒有保證轉出來的經緯度一定是正確的,而地址的資料來源又有夠髒.......
累積了幾年資料後,某次把資料提供給其他單位參考,還被抓出一堆錯務的經緯度,有些甚至在靠近北極的海上......之後才把經緯度加上了查驗
做法:
1.取出要轉的地址
2.經GOOGLE轉出經緯度
3.把轉回來的經緯度再次利用GOOGLE轉回一組新地址
4.將原地址與經由GOOGLE轉回來的新地址比對,如果原地址的縣市與鄉鎮同時存在於新地址的前十二個字,則認定縣市鄉鎮正確
5.GOOGLE傳回來的地址中,大部分的”臺”都是用”台”,所以在比對的時候有把轉回來的新地址中”台”替換成”臺”再比對

其中的url,http://maps.google.com//maps/api/geocode/json?address=是可以直接用的,後面可以帶的參數有很多,比如說在查地址的時候可以限定回傳的國家,或是在邊界的時候怎麼拚斷,以及回傳文字的語言。

在經緯度反查地址的時候,台灣記得要加上&language=zh-tw,不然傳回來的地址會是英文的

免費的用戶每天有2500筆,企業用戶有10萬筆,但不管免費或企業,google每秒都有設查詢次數限制,要注意

下面是地址查經緯度的部分,經緯度查地址其實是一模一樣的東西,這是第一版,後來有把兩個函式合併

可以留意一下GOOGLE回傳的json中包含"location_type"這個欄位,這是GOOGLE的精準度,共分成四個等級,節錄如下
  • location_type 會儲存指定位置的其他相關資料,目前所支援的值如下:
    • "ROOFTOP" 表示傳回的結果是精準的地理編碼,因為結果中位置資訊的精確範圍已縮小至街道地址。
    • "RANGE_INTERPOLATED" 表示傳回的結果反映的是插入在兩個精確定點之間 (例如十字路口) 的約略位置 (通常會在街道上)。如果 Geocoder 無法取得街道地址的精確定點地理編碼,就會傳回插入的結果。
    • "GEOMETRIC_CENTER" 表示傳回的結果是結果的幾何中心,例如折線 (例如街道) 和多邊形 (區域)。
    • "APPROXIMATE" 表示傳回的結果是約略位置。
節錄自https://developers.google.com/maps/documentation/geocoding/?hl=zh-tw,這是google的geocoding API說明文件,大力推薦先看,當中有更多欄位說明與使用參考

以我個人的經驗,即使是最差的APPROXIMATE,縣市鄉鎮都有可能是對的,所以如果只是要求到縣市鄉鎮對,第四等級的經緯度還是可以用,但如果需要畫圖,比如說經銷商分布圖、公車站牌密度圖或是熱區,則不建議使用第三跟第四等級,因為他們會是幾何與約略位置,我的經驗是他們會集中在"某一點",造成錯誤的熱區或密度結果

/// <summary>
    /// 地址查經緯度
    /// </summary>
    /// <param name="StrAddress">地址</param>
    /// <param name="GoogleResult">回傳結果</param>
    /// <param name="location_type">精準度</param>
    /// <returns></returns>
    public static double[] getLatLng(string StrAddress, out string GoogleResult, out string location_type)
    {
        string address = "";
        string StrClientID = "填購買的google id";
        string keyString = "填購買的google key";
        //google地理服務的網址,下面這串網址就能直接用了
        string urlString = "http://maps.google.com//maps/api/geocode/json?address=" + address + "&sensor=false&client=" + StrClientID;
        //下面這步是企業用戶登入時要作的sign in
        string StrCryptoKey = Sign(urlString, keyString);

        var url = String.Format(StrCryptoKey);
        string result = String.Empty;
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
        using (var response = request.GetResponse())
        using (StreamReader sr = new StreamReader(response.GetResponseStream()))
        {
            //Json格式: 請參考http://code.google.com/intl/zh-TW/apis/maps/documentation/geocoding/
            result = sr.ReadToEnd();
        }
        //將Json字串轉成物件
        GoogleGeocodingAPI.RootObject rootObj = JsonConvert.DeserializeObject<GoogleGeocodingAPI.RootObject>(result);
        double lat, lng;
        if (rootObj.status == "OK")
        {
            GoogleResult = "OK";
            //從results開始往下找
            lat = rootObj.results[0].geometry.location.lat;//緯度
            lng = rootObj.results[0].geometry.location.lng;//經度
            location_type = rootObj.results[0].geometry.location_type.ToString();
        }
        else
        {
            GoogleResult = rootObj.status;
            lat = 0;
            lng = 0;
            location_type = "";
        }
        double[] latLng = { lat, lng };
        return latLng;
    }

下面是比對,想法是只要抓前半段地址比對就好,不需要抓到整個地址,再者由於在臺灣,GOOGLE傳回來的地址前面會有三碼郵遞區號跟"台灣"兩字,所以抓前12個字來比

如果縣市跟鄉鎮都命中就認為是對的

    /// <summary>
    /// 比對縣市鄉鎮有沒有包含在反查回來的地址
    /// </summary>
    /// <param name="city">縣市</param>
    /// <param name="area">鄉鎮</param>
    /// <param name="FixedAddress">地址</param>
    /// <returns></returns>
    public static bool CheckAddressCorrect(string city, string area, string FixedAddress)
    {
        bool result = false;
        string address = "";
        
        if (FixedAddress.Length > 12)
        { address = FixedAddress.Substring(0, 12); }
        else
        { address = FixedAddress; }

        //GOOGLE傳回來的地址會是"台" 不知到甚麼時候才會改回"臺",所以替換掉
        FixedAddress = FixedAddress.Replace('台', '臺'); 

        //高雄市三民區因業務需要,有分成三民東西區,替換掉
        if (city == "高雄市" && area == "三民東區")
            area = area.Replace("三民東區", "三民區");
        if (city == "高雄市" &&  area == "三民西區")
            area = area.Replace("三民西區", "三民區");

        //比對縣市鄉鎮
        if (FixedAddress.IndexOf(city, StringComparison.OrdinalIgnoreCase) > 0 && FixedAddress.IndexOf(area, StringComparison.OrdinalIgnoreCase) > 0)
        { result = true; }

        return result;
    }

大概就是這樣,我曾經碰過短時間內GOOGLE一直回傳"limit query"的錯誤訊息,不過過一下子就好了,與同事討論也無解,只能猜測是當時GOOGLE比較忙吧XD~

文章連結備份

Hash & Salt:http://blog.jobbole.com/61872/

2014年6月17日 星期二

把DataSet轉CSV的winform小程式

逗號分隔值(Comma-Separated Values,CSV,有時也稱為字元分隔值,因為分隔字元也可以不是逗號),其文件以純文本形式存儲表格數據(數字和文本)
已上擷取自維基百科 逗號分隔值

dataset轉CSV大至上很不難,但我一直沒能在GOOGLE上直接找到預期功能的元件或工具,剛好前幾天有需求,就順手做了一個winform小程式

用逗號分隔,可以自行決定是否輸入第一行的值(並沒有檢查第一行值的數量是否等於dataset每行的資料數量)

所有資料都用雙引號(\")包起來,同時資料內的雙引號改為雙雙引號(\"=>\"\"),另外有特別處理一下斷行符(\r\n),把他替換成空白;輸出的CSV檔名預設為系統時間的時+分+秒。

裡面並沒有針對資料格式做判斷,比如說有沒有哪一行資料數量跟其他行不一樣;或是第一行的資料格式有沒有異常、資料是否包含可能會發生錯誤的字元等等,只是做為一個產出CSV的小工具而已。不過一如往常會有原始檔,可以改成自己要用的格式或加上檢查。

使用 .net framework 2.0

我的雲端硬碟

裡面的 DataSetToCSV_R.rar

//選擇存檔路徑
        private void btnSavePath_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog path = new FolderBrowserDialog();
            path.ShowDialog();
            tbxSavePath.Text = path.SelectedPath;
        }

        private void btnMake_Click(object sender, EventArgs e)
        {
            strDBComm = "Server = " + tbxServer.Text.Trim() + "; database = " + tbxDataBase.Text.Trim() + "; uid=" + tbxUID.Text.Trim() + "; pwd=" + tbxPassword.Text.Trim() + "; Max Pool Size = 500; timeout = 180000";            
            tData = SelectSQLCommon_DT(tbxQuery.Text.Replace("\r\n", ""));
            strCSVSavePath = tbxSavePath.Text.Trim() + "\\" + DateTime.Now.Hour + DateTime.Now.Minute + DateTime.Now.Second + ".csv";
            StreamWriter sw = new StreamWriter(strCSVSavePath, false, Encoding.Default);
            if (rbtFirstLineTrue.Checked)
            {
                sw.Write("\"");
                sw.Write(tbxFirstLine.Text.Trim().Replace(",", "\",\""));
                sw.Write("\"");                
            }
            sw.Write(sw.NewLine);
            foreach (DataRow dr in tData.Rows)
            {
                if (tData.Columns.Count > 0 && !Convert.IsDBNull(dr[0]))
                    sw.Write("\"" + ReplaceSign(Convert.ToString(dr[0])).Trim());
                for (int i = 1; i < tData.Columns.Count; i++)
                {
                    sw.Write("\",\"" + ReplaceSign(Convert.ToString(dr[i])).Trim());
                }
                sw.Write("\"");
                sw.Write(sw.NewLine);
            }
            sw.Close();
        }

        //讀取資料-回傳DataTable
        public DataTable SelectSQLCommon_DT(string _strSQL)
        {
            DataTable _dtData = new DataTable();

            SqlConnection _myConnection = new SqlConnection(strDBComm);
            if (_myConnection.State == ConnectionState.Closed) _myConnection.Open();
            SqlDataAdapter _daCommon = new SqlDataAdapter(_strSQL, _myConnection);
            _daCommon.SelectCommand.CommandTimeout = 3600;
            _daCommon.Fill(_dtData);

            _daCommon.Dispose();
            _myConnection.Close();
            _myConnection.Dispose();
            return _dtData;
        }

        //替換掉\r\n 與加上雙引號
        public string ReplaceSign(string OldString)
        {
            return OldString.Replace("\r\n", " ").Replace("\"", "\"\"");
        }

        private void rbtFirstLineTrue_CheckedChanged(object sender, EventArgs e)
        {
            lblFirstLine.Visible = true;
            tbxFirstLine.Visible = true;
        }

        private void rbtFirstLineFalse_CheckedChanged(object sender, EventArgs e)
        {
            lblFirstLine.Visible = false;
            tbxFirstLine.Visible = false;
        }

2013年1月22日 星期二

排程程式

請想像這篇會是一篇關於會自動抓指定位置的文字檔並修改後存資料庫的東西
................墮落成這樣了

SQL按月份分組、加總各月份資料


SELECT C.MarkName
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'1月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'2月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'3月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'4月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'5月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'6月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'7月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'8月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'9月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'10月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'11月'
,SUM(Case MONTH(AcceptDate) when '1' then '1' else 0 end) as'12月'
,COUNT(C.MarkName)as '總合'
FROM (SELECT * FROM CasePublicData ) C
where AcceptDate between '2010/1/1' and '2010/12/31'
group by C.MarkName
order by C.MarkName

MarkName是廠商名字、AcceptDate是受理日期